From dc55bcee4fcf3fd9e1caeca161fcc00f9f886ab7 Mon Sep 17 00:00:00 2001 From: Andy Zivkovic Date: Sat, 8 Nov 2025 08:06:43 +1030 Subject: [PATCH 1/5] LegacyPackageReferenceProject adapter for PackageSpecFactory --- .../GlobalSuppressions.cs | 1 - ...VsManagedLanguagesProjectSystemServices.cs | 19 +- ...ILegacyPackageReferenceProjectServices .cs | 12 + .../Projects/LegacyPackageReferenceProject.cs | 43 +- .../LegacyPackageReferenceProjectProvider.cs | 2 +- .../Projects/LegacyProjectAdapter.cs | 303 +++++++++ .../Utility/EnvDTEProjectUtility.cs | 10 + .../MSBuildStaticGraphRestore.cs | 2 +- .../PublicAPI/net472/PublicAPI.Shipped.txt | 4 +- .../PublicAPI/net8.0/PublicAPI.Shipped.txt | 4 +- .../NuGet.Commands/RestoreCommand/IItem.cs | 2 +- .../RestoreCommand/ITargetFramework.cs | 2 +- .../Utility/PackageSpecFactory.cs | 49 +- .../ProjectFactories.cs | 25 +- .../LegacyPackageReferenceProjectTests.cs | 600 ++++++++++++------ ...gacyPackageReferenceRestoreUtilityTests.cs | 251 +++++--- .../NuGetProjectManagerServiceTests.cs | 35 +- .../DotnetRestoreTests.cs | 11 +- .../MsbuildRestoreTaskTests.cs | 17 +- .../TestProjectSystemServices.cs | 6 +- 20 files changed, 1035 insertions(+), 363 deletions(-) create mode 100644 src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/ILegacyPackageReferenceProjectServices .cs create mode 100644 src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyProjectAdapter.cs diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/GlobalSuppressions.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/GlobalSuppressions.cs index 7a757931d0a..ff89e49aa28 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/GlobalSuppressions.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/GlobalSuppressions.cs @@ -29,7 +29,6 @@ [assembly: SuppressMessage("Build", "CA1062:In externally visible method 'Task> EnvDTESolutionUtility.GetAllEnvDTEProjectsAsync(DTE dte)', validate parameter 'dte' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGet.PackageManagement.VisualStudio.EnvDTESolutionUtility.GetAllEnvDTEProjectsAsync(EnvDTE.DTE)~System.Threading.Tasks.Task{System.Collections.Generic.IEnumerable{EnvDTE.Project}}")] [assembly: SuppressMessage("Build", "CA1031:Modify 'GetFrameworkAssemblies' to catch a more specific allowed exception type, or rethrow the exception.", Justification = "", Scope = "member", Target = "~M:NuGet.PackageManagement.VisualStudio.FrameworkAssemblyResolver.GetFrameworkAssemblies(System.String)~System.Collections.Generic.List{NuGet.PackageManagement.VisualStudio.FrameworkAssembly}")] [assembly: SuppressMessage("Build", "CA1031:Modify 'RemoveReferenceAsync' to catch a more specific allowed exception type, or rethrow the exception.", Justification = "", Scope = "member", Target = "~M:NuGet.PackageManagement.VisualStudio.FSharpProjectSystem.RemoveReferenceAsync(System.String)~System.Threading.Tasks.Task")] -[assembly: SuppressMessage("Build", "CA1062:In externally visible method 'LegacyPackageReferenceProject.LegacyPackageReferenceProject(IVsProjectAdapter vsProjectAdapter, string projectId, INuGetProjectServices projectServices, IVsProjectThreadingService threadingService)', validate parameter 'vsProjectAdapter' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGet.PackageManagement.VisualStudio.LegacyPackageReferenceProject.#ctor(NuGet.VisualStudio.IVsProjectAdapter,System.String,NuGet.ProjectManagement.INuGetProjectServices,NuGet.PackageManagement.VisualStudio.IVsProjectThreadingService)")] [assembly: SuppressMessage("Build", "CA1062:In externally visible method 'Task LegacyPackageReferenceProject.AddFileToProjectAsync(string filePath)', validate parameter 'filePath' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGet.PackageManagement.VisualStudio.LegacyPackageReferenceProject.AddFileToProjectAsync(System.String)~System.Threading.Tasks.Task")] [assembly: SuppressMessage("Build", "CA1822:Member GetConfigFilePaths does not access instance data and can be marked as static (Shared in VisualBasic)", Justification = "", Scope = "member", Target = "~M:NuGet.PackageManagement.VisualStudio.LegacyPackageReferenceProject.GetConfigFilePaths(NuGet.Configuration.ISettings)~System.Collections.Generic.IList{System.String}")] [assembly: SuppressMessage("Build", "CA1062:In externally visible method 'Task LegacyPackageReferenceProject.UninstallPackageAsync(PackageIdentity packageIdentity, INuGetProjectContext _, CancellationToken token)', validate parameter 'packageIdentity' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGet.PackageManagement.VisualStudio.LegacyPackageReferenceProject.UninstallPackageAsync(NuGet.Packaging.Core.PackageIdentity,NuGet.ProjectManagement.INuGetProjectContext,System.Threading.CancellationToken)~System.Threading.Tasks.Task{System.Boolean}")] diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectServices/VsManagedLanguagesProjectSystemServices.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectServices/VsManagedLanguagesProjectSystemServices.cs index 3a16be21a86..9ab128ea204 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectServices/VsManagedLanguagesProjectSystemServices.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/ProjectServices/VsManagedLanguagesProjectSystemServices.cs @@ -15,6 +15,7 @@ using NuGet.Common; using NuGet.Frameworks; using NuGet.LibraryModel; +using NuGet.PackageManagement.VisualStudio.Projects; using NuGet.ProjectManagement; using NuGet.ProjectModel; using NuGet.Versioning; @@ -32,12 +33,14 @@ internal class VsManagedLanguagesProjectSystemServices : , IProjectSystemCapabilities , IProjectSystemReferencesReader , IProjectSystemReferencesService + , ILegacyPackageReferenceProjectServices { private static readonly string[] ReferenceMetadata; private readonly IVsProjectAdapter _vsProjectAdapter; private readonly IVsProjectThreadingService _threadingService; - private readonly VSProject4 _vsProject4; + + public VSProject4 Project4 { get; } public bool SupportsPackageReferences => true; @@ -85,7 +88,7 @@ public VsManagedLanguagesProjectSystemServices( _vsProjectAdapter = vsProjectAdapter; _threadingService = threadingService; - _vsProject4 = vsProject4; + Project4 = vsProject4; ScriptService = new VsProjectScriptHostService(vsProjectAdapter, scriptExecutor); @@ -99,7 +102,7 @@ public async Task> GetPackageReferencesAsync( await _threadingService.JoinableTaskFactory.SwitchToMainThreadAsync(); - var installedPackages = _vsProject4.PackageReferences?.InstalledPackages; + var installedPackages = Project4.PackageReferences?.InstalledPackages; if (installedPackages == null) { @@ -113,7 +116,7 @@ public async Task> GetPackageReferencesAsync( .Where(r => !string.IsNullOrEmpty(r)) .Select(installedPackage => { - if (_vsProject4.PackageReferences.TryGetReference( + if (Project4.PackageReferences.TryGetReference( installedPackage, ReferenceMetadata, out var version, @@ -141,13 +144,13 @@ public async Task> GetProjectReferencesAsyn { await _threadingService.JoinableTaskFactory.SwitchToMainThreadAsync(); - if (_vsProject4.References == null) + if (Project4.References == null) { return Array.Empty(); } var references = new List(); - foreach (Reference6 r in _vsProject4.References.Cast()) + foreach (Reference6 r in Project4.References.Cast()) { if (r.SourceProject != null && await EnvDTEProjectUtility.IsSupportedAsync(r.SourceProject)) { @@ -317,7 +320,7 @@ private void AddOrUpdatePackageReference(string packageName, VersionRange packag // - specify a metadata element name with a value => add/replace that metadata item on the package reference // - specify a metadata element name with no value => remove that metadata item from the project reference // - don't specify a particular metadata name => if it exists on the package reference, don't change it (e.g. for user defined metadata) - _vsProject4.PackageReferences.AddOrUpdate( + Project4.PackageReferences.AddOrUpdate( packageName, packageVersion.OriginalString ?? packageVersion.ToShortString(), metadataElements, @@ -330,7 +333,7 @@ public async Task RemovePackageReferenceAsync(string packageName) await _threadingService.JoinableTaskFactory.SwitchToMainThreadAsync(); - _vsProject4.PackageReferences.Remove(packageName); + Project4.PackageReferences.Remove(packageName); } private bool IsCentralPackageManagementVersionsEnabled() diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/ILegacyPackageReferenceProjectServices .cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/ILegacyPackageReferenceProjectServices .cs new file mode 100644 index 00000000000..d28f94d833e --- /dev/null +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/ILegacyPackageReferenceProjectServices .cs @@ -0,0 +1,12 @@ +// 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 NuGet.ProjectManagement; +using VSLangProj150; + +namespace NuGet.PackageManagement.VisualStudio.Projects; + +public interface ILegacyPackageReferenceProjectServices : INuGetProjectServices +{ + VSProject4 Project4 { get; } +} diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProject.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProject.cs index dbe9c3a164f..587e3eba883 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProject.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProject.cs @@ -16,10 +16,13 @@ using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Threading; using NuGet.Commands; +using NuGet.Commands.Restore; +using NuGet.Commands.Restore.Utility; using NuGet.Common; using NuGet.Configuration; using NuGet.Frameworks; using NuGet.LibraryModel; +using NuGet.PackageManagement.VisualStudio.Projects; using NuGet.PackageManagement.VisualStudio.Utility; using NuGet.Packaging; using NuGet.Packaging.Core; @@ -41,14 +44,17 @@ public sealed class LegacyPackageReferenceProject : PackageReferenceProject GetConfigFilePaths(ISettings settings) /// Emulates a JSON deserialization from project.json to PackageSpec in a post-project.json world /// private async Task GetPackageSpecAsync(ISettings settings) + { + if (_usePackageSpecFactory) + { + return await GetPackageSpecWithFactoryAsync(settings); + } + else + { + return await GetPackageSpecClassicAsync(settings); + } + } + + private async Task GetPackageSpecClassicAsync(ISettings settings) { await _threadingService.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -595,6 +622,14 @@ private async Task GetPackageSpecAsync(ISettings settings) }; } + private async Task GetPackageSpecWithFactoryAsync(ISettings settings) + { + await _threadingService.JoinableTaskFactory.SwitchToMainThreadAsync(); + IProject project = new LegacyProjectAdapter(_vsProjectAdapter, _projectServices.Project4); + PackageSpec packageSpec = PackageSpecFactory.GetPackageSpec(project, settings); + return packageSpec; + } + internal static ImmutableArray ApplyCentralVersionInformation(ImmutableArray packageReferences, IReadOnlyDictionary centralPackageVersions) { if (packageReferences.IsDefault) diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProjectProvider.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProjectProvider.cs index 7644508b53a..314df3f8216 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProjectProvider.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProjectProvider.cs @@ -81,7 +81,7 @@ public NuGetProject TryCreateNuGetProject( /// /// Is this project a non-CPS package reference based csproj? /// - private INuGetProjectServices TryCreateProjectServices( + private VsManagedLanguagesProjectSystemServices TryCreateProjectServices( IVsProjectAdapter vsProjectAdapter, bool forceCreate) { ThreadHelper.ThrowIfNotOnUIThread(); diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyProjectAdapter.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyProjectAdapter.cs new file mode 100644 index 00000000000..6af37ef4061 --- /dev/null +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyProjectAdapter.cs @@ -0,0 +1,303 @@ +// 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. + +#nullable enable + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft; +using NuGet.Commands.Restore; +using NuGet.ProjectManagement; +using NuGet.VisualStudio; +using VSLangProj150; + +namespace NuGet.PackageManagement.VisualStudio.Projects; + +internal class LegacyProjectAdapter : IProject +{ + private readonly IVsProjectAdapter _projectAdapter; + + public LegacyProjectAdapter(IVsProjectAdapter projectAdapter, VSProject4 project4) + { + _projectAdapter = projectAdapter; + OuterBuild = new TargetFrameworkAdapter(_projectAdapter, project4); + TargetFrameworks = new Dictionary(StringComparer.OrdinalIgnoreCase) + { + [""] = OuterBuild + }; + } + + public string FullPath => _projectAdapter.FullProjectPath; + + public string Directory => _projectAdapter.ProjectDirectory; + + public ITargetFramework OuterBuild { get; } + + public IReadOnlyDictionary TargetFrameworks { get; } + + private class TargetFrameworkAdapter : ITargetFramework + { + private readonly IVsProjectAdapter _projectAdapter; + private readonly VSProject4 _project4; + + public TargetFrameworkAdapter(IVsProjectAdapter projectAdapter, VSProject4 project4) + { + _projectAdapter = projectAdapter; + _project4 = project4; + } + + public IReadOnlyList GetItems(string itemType) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + + if (!ItemMetadataNames.TryGetValue(itemType, out var metadataNames)) + { + throw new ArgumentException($"Unknown item type: {itemType}", nameof(itemType)); + } + + if (itemType.Equals(ProjectItems.PackageReference, StringComparison.OrdinalIgnoreCase)) + { + var packageReferences = GetPackageReferences(metadataNames); + return packageReferences; + } + else if (itemType.Equals(ProjectItems.ProjectReference, StringComparison.OrdinalIgnoreCase)) + { + var projectReferences = GetProjectReferences(metadataNames); + return projectReferences; + } + + var items = _projectAdapter.GetBuildItemInformation(itemType, metadataNames); + var result = new List(items is ICollection collection ? collection.Count : 0); + foreach (var (itemId, itemMetadata) in items) + { + var metadataDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + for (int i = 0; i < metadataNames.Length; i++) + { + string? value = itemMetadata[i]; + if (!string.IsNullOrEmpty(value)) + { + metadataDictionary[metadataNames[i]] = itemMetadata[i]; + } + } + + var itemAdapter = new ItemAdapter + { + Identity = itemId, + Metadata = metadataDictionary + }; + result.Add(itemAdapter); + } + + return result; + } + + private IReadOnlyList GetPackageReferences(string[] metadataNames) + { + if (_project4.PackageReferences is null) + { + return []; + } + + string[]? installedPackages = (string[]?)_project4.PackageReferences.InstalledPackages; + + if (installedPackages is null || installedPackages.Length == 0) + { + return []; + } + + List packageReferences = new List(installedPackages.Length); + foreach (var packageId in installedPackages) + { + if (_project4.PackageReferences.TryGetReference(packageId, metadataNames, out string version, out var metadataElements, out var metadataValues)) + { + Dictionary metadataDictionary = ToMetadataDictionary(metadataElements, metadataValues); + + if (!string.IsNullOrEmpty(version)) + { + metadataDictionary[ProjectBuildProperties.Version] = version; + } + + var itemAdapter = new ItemAdapter + { + Identity = packageId, + Metadata = metadataDictionary + }; + packageReferences.Add(itemAdapter); + } + } + + return packageReferences; + } + + private IReadOnlyList GetProjectReferences(string[] metadataNames) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + + if (_project4.References is null) + { + return []; + } + + List references = new List(); + foreach (Reference6 projectReference in _project4.References) + { + if (projectReference.SourceProject != null && EnvDTEProjectUtility.IsSupported(projectReference.SourceProject, _projectAdapter.VsHierarchy)) + { + Array metadataElements; + Array metadataValues; + projectReference.GetMetadata(metadataNames, out metadataElements, out metadataValues); + var metadataDictionary = ToMetadataDictionary(metadataElements, metadataValues); + + var itemAdapter = new ItemAdapter + { + Identity = projectReference.SourceProject.FullName, + Metadata = metadataDictionary + }; + + references.Add(itemAdapter); + } + } + + return references; + } + + private static Dictionary ToMetadataDictionary(Array names, Array values) + { + Assumes.True(names.Length == values.Length); + + var dictionary = new Dictionary(names.Length, StringComparer.OrdinalIgnoreCase); + + for (int i = 0; i < names.Length; i++) + { + string? name = names.GetValue(i) as string; + string? value = values.GetValue(i) as string; + if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(value)) + { + dictionary[name!] = value!; + } + } + + return dictionary; + } + + public string? GetProperty(string propertyName) + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + string? value; + if (FallbackProperties.Contains(propertyName)) + { +#pragma warning disable CS0618 // Type or member is obsolete + value = _projectAdapter.BuildProperties.GetPropertyValueWithDteFallback(propertyName); +#pragma warning restore CS0618 // Type or member is obsolete + } + else + { + value = _projectAdapter.BuildProperties.GetPropertyValue(propertyName); + } + + if (!string.IsNullOrEmpty(value)) + { + return value; + } + + if (propertyName.Equals(ProjectBuildProperties.RestoreProjectStyle, StringComparison.OrdinalIgnoreCase)) + { + return "PackageReference"; + } + + return null; + } + + // Do not add new properties here. New properties should only use new project system APIs. + // In fact, ideally we should be migrating these properties + private static readonly ImmutableHashSet FallbackProperties = [ + ProjectBuildProperties.PackageId, + ProjectBuildProperties.AssemblyName, + ProjectBuildProperties.RestorePackagesPath, + ProjectBuildProperties.RestoreSources, + ProjectBuildProperties.RestoreAdditionalProjectSources, + ProjectBuildProperties.RestoreFallbackFolders, + ProjectBuildProperties.RestoreAdditionalProjectFallbackFolders, + ProjectBuildProperties.PackageTargetFallback, + ProjectBuildProperties.AssetTargetFallback, + ProjectBuildProperties.ManagePackageVersionsCentrally, + ProjectBuildProperties.RuntimeIdentifier, + ProjectBuildProperties.RuntimeIdentifiers, + ProjectBuildProperties.RuntimeSupports, + ProjectBuildProperties.TreatWarningsAsErrors, + ProjectBuildProperties.NoWarn, + ProjectBuildProperties.WarningsAsErrors, + ProjectBuildProperties.WarningsNotAsErrors, + ProjectBuildProperties.RestorePackagesWithLockFile, + ProjectBuildProperties.NuGetLockFilePath, + ProjectBuildProperties.RestoreLockedMode, + ProjectBuildProperties.CentralPackageVersionOverrideEnabled, + ProjectBuildProperties.CentralPackageTransitivePinningEnabled, + ProjectBuildProperties.MSBuildProjectExtensionsPath, + ProjectBuildProperties.PackageVersion, + ProjectBuildProperties.Version, + ProjectBuildProperties.TargetPlatformIdentifier, + ProjectBuildProperties.TargetPlatformVersion, + ProjectBuildProperties.TargetPlatformMinVersion, + ProjectBuildProperties.TargetFrameworkMoniker, + ]; + } + + private record ItemAdapter : IItem + { + public required string Identity { get; init; } + internal required IReadOnlyDictionary Metadata { get; init; } + + public string? GetMetadata(string name) + { + if (Metadata.TryGetValue(name, out var value)) + { + return value; + } + else + { + return null; + } + } + } + + private static readonly string[] PackageReferenceMetadataNames = + [ + "IsImplicitlyDefined", + "Version", + "VersionOverride", + "GeneratePathProperty", + "Aliases", + "IncludeAssets", + "ExcludeAssets", + "PrivateAssets", + "NoWarn", + ]; + + private static readonly string[] FrameworkReferenceMetadataNames = ["PrivateAssets"]; + + private static readonly string[] VersionOnlyMetadataNames = ["Version"]; + + private static readonly string[] ProjectReferenceMetadataNames = + [ + "ReferenceOutputAssembly", + "FullPath", + "ExcludeAssets", + "IncludeAssets", + "PrivateAssets", + ]; + + internal static readonly IReadOnlyDictionary ItemMetadataNames = + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + [ProjectItems.PackageReference] = PackageReferenceMetadataNames, + [ProjectItems.PrunePackageReference] = VersionOnlyMetadataNames, + [ProjectItems.PackageDownload] = VersionOnlyMetadataNames, + [ProjectItems.FrameworkReference] = FrameworkReferenceMetadataNames, + [ProjectItems.PackageVersion] = VersionOnlyMetadataNames, + [ProjectItems.NuGetAuditSuppress] = [], + [ProjectItems.ProjectReference] = ProjectReferenceMetadataNames, + }; +} diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/EnvDTEProjectUtility.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/EnvDTEProjectUtility.cs index 2cf4b051f29..7ff1f74a139 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/EnvDTEProjectUtility.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/EnvDTEProjectUtility.cs @@ -394,6 +394,16 @@ public static async Task IsSupportedAsync(EnvDTE.Project envDTEProject) Assumes.Present(envDTEProject); await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); var hierarchy = await envDTEProject.ToVsHierarchyAsync(); + return IsSupported(envDTEProject, hierarchy); + } + + public static bool IsSupported(EnvDTE.Project envDTEProject, IVsHierarchy hierarchy) + { + ThreadHelper.ThrowIfNotOnUIThread(); + + Assumes.Present(envDTEProject); + Assumes.Present(hierarchy); + if (VsHierarchyUtility.IsProjectCapabilityCompliant(hierarchy)) { return true; diff --git a/src/NuGet.Core/NuGet.Build.Tasks.Console/MSBuildStaticGraphRestore.cs b/src/NuGet.Core/NuGet.Build.Tasks.Console/MSBuildStaticGraphRestore.cs index bb7f6f86505..32d2ad58ea8 100644 --- a/src/NuGet.Core/NuGet.Build.Tasks.Console/MSBuildStaticGraphRestore.cs +++ b/src/NuGet.Core/NuGet.Build.Tasks.Console/MSBuildStaticGraphRestore.cs @@ -791,7 +791,7 @@ internal static List GetTargetFrameworkInfos(IReadOn /// A for the specified project if they could be loaded, otherwise null. private DependencyGraphSpec GetDependencyGraphSpec(string entryProjectPath, IDictionary globalProperties, bool interactive, string binaryLoggerParameters, IEnvironmentVariableReader environmentVariableReader) { - string envVar = environmentVariableReader.GetEnvironmentVariable("NUGET_USE_NEW_PACKAGESPEC_FACTORY"); + string envVar = environmentVariableReader.GetEnvironmentVariable(PackageSpecFactory.EnvironmentVariableName); if (!string.Equals(envVar, bool.FalseString, StringComparison.OrdinalIgnoreCase)) { return GetDependencyGraphSpec( diff --git a/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Shipped.txt b/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Shipped.txt index 86b8430a414..7d6f51120d0 100644 --- a/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Shipped.txt +++ b/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Shipped.txt @@ -445,7 +445,7 @@ NuGet.Commands.ResolverRequest ~NuGet.Commands.ResolverRequest.Requestor.get -> NuGet.LibraryModel.LibraryIdentity ~NuGet.Commands.ResolverRequest.ResolverRequest(NuGet.LibraryModel.LibraryIdentity requestor, NuGet.LibraryModel.LibraryRange request) -> void NuGet.Commands.Restore.IItem -NuGet.Commands.Restore.IItem.GetMetadata(string! name) -> string! +NuGet.Commands.Restore.IItem.GetMetadata(string! name) -> string? NuGet.Commands.Restore.IItem.Identity.get -> string! NuGet.Commands.Restore.IProject NuGet.Commands.Restore.IProject.Directory.get -> string! @@ -454,7 +454,7 @@ NuGet.Commands.Restore.IProject.OuterBuild.get -> NuGet.Commands.Restore.ITarget NuGet.Commands.Restore.IProject.TargetFrameworks.get -> System.Collections.Generic.IReadOnlyDictionary! NuGet.Commands.Restore.ITargetFramework NuGet.Commands.Restore.ITargetFramework.GetItems(string! itemType) -> System.Collections.Generic.IReadOnlyList! -NuGet.Commands.Restore.ITargetFramework.GetProperty(string! propertyName) -> string! +NuGet.Commands.Restore.ITargetFramework.GetProperty(string! propertyName) -> string? NuGet.Commands.Restore.Utility.PackageSpecFactory NuGet.Commands.RestoreArgs ~NuGet.Commands.RestoreArgs.AdditionalMessages.get -> System.Collections.Generic.IReadOnlyList diff --git a/src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Shipped.txt b/src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Shipped.txt index c87781197eb..8a4e66760dd 100644 --- a/src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Shipped.txt +++ b/src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Shipped.txt @@ -445,7 +445,7 @@ NuGet.Commands.ResolverRequest ~NuGet.Commands.ResolverRequest.Requestor.get -> NuGet.LibraryModel.LibraryIdentity ~NuGet.Commands.ResolverRequest.ResolverRequest(NuGet.LibraryModel.LibraryIdentity requestor, NuGet.LibraryModel.LibraryRange request) -> void NuGet.Commands.Restore.IItem -NuGet.Commands.Restore.IItem.GetMetadata(string! name) -> string! +NuGet.Commands.Restore.IItem.GetMetadata(string! name) -> string? NuGet.Commands.Restore.IItem.Identity.get -> string! NuGet.Commands.Restore.IProject NuGet.Commands.Restore.IProject.Directory.get -> string! @@ -454,7 +454,7 @@ NuGet.Commands.Restore.IProject.OuterBuild.get -> NuGet.Commands.Restore.ITarget NuGet.Commands.Restore.IProject.TargetFrameworks.get -> System.Collections.Generic.IReadOnlyDictionary! NuGet.Commands.Restore.ITargetFramework NuGet.Commands.Restore.ITargetFramework.GetItems(string! itemType) -> System.Collections.Generic.IReadOnlyList! -NuGet.Commands.Restore.ITargetFramework.GetProperty(string! propertyName) -> string! +NuGet.Commands.Restore.ITargetFramework.GetProperty(string! propertyName) -> string? NuGet.Commands.Restore.Utility.PackageSpecFactory NuGet.Commands.RestoreArgs ~NuGet.Commands.RestoreArgs.AdditionalMessages.get -> System.Collections.Generic.IReadOnlyList diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/IItem.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/IItem.cs index 5013c9a6eb6..4c16497d919 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/IItem.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/IItem.cs @@ -16,6 +16,6 @@ public interface IItem /// Get the evaluated value of a metadata on this item. /// The name of the metadata whose value is retrieved. /// The evaluated value of the given metadata for this item, or if no metadata exists with the given name. - string GetMetadata(string name); + string? GetMetadata(string name); } } diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/ITargetFramework.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/ITargetFramework.cs index f310cce6463..9e04447bc6b 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/ITargetFramework.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/ITargetFramework.cs @@ -15,7 +15,7 @@ public interface ITargetFramework /// Get the value for a property in the project. /// The name of the property /// The value of the requested property, or if the property was not defined. - string GetProperty(string propertyName); + string? GetProperty(string propertyName); /// Get all items of a given type. /// The item type. For example, PackageReference, PackageVersion, etc. diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/PackageSpecFactory.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/PackageSpecFactory.cs index 1d690ed9de2..ecb59c1e3bc 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/PackageSpecFactory.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/PackageSpecFactory.cs @@ -32,6 +32,13 @@ namespace NuGet.Commands.Restore.Utility /// public static class PackageSpecFactory { + /// + /// Environment variable to tell NuGet to not use the new PackageSpec factory. + /// If you have to use this, make sure that there's a issue in https://github.com/NuGet/Home to make sure + /// bugs are fixed before the old package spec methods are removed. + /// + public const string EnvironmentVariableName = "NUGET_USE_NEW_PACKAGESPEC_FACTORY"; + /// /// Convert an MSBuild project to a PackageSpec. /// @@ -190,12 +197,14 @@ private static (ProjectRestoreMetadata? RestoreMetadata, List p.GetItems("PackageReference").Any()); + string? projectName = project.OuterBuild.GetProperty("MSBuildProjectName"); + if (projectName is null) throw new Exception("Something went wrong. MSBuildProjectName should always have a value, but did not."); (ProjectStyle ProjectStyle, string? PackagesConfigFilePath) projectStyleResult = GetProjectRestoreStyle( restoreProjectStyle: projectStyleOrNull, hasPackageReferenceItems: hasPackageReferenceItems, projectDirectory: project.Directory, - projectName: project.OuterBuild.GetProperty("MSBuildProjectName")); + projectName: projectName); return (projectStyleResult.ProjectStyle, projectStyleResult.PackagesConfigFilePath); } @@ -281,9 +290,9 @@ private static bool GetPackagePruningDefault(IProject project) private static RestoreAuditProperties? GetRestoreAuditProperties(IProject project) { - string enableAudit = project.OuterBuild.GetProperty("NuGetAudit"); - string auditLevel = project.OuterBuild.GetProperty("NuGetAuditLevel"); - string auditMode = GetAuditMode(project); + string? enableAudit = project.OuterBuild.GetProperty("NuGetAudit"); + string? auditLevel = project.OuterBuild.GetProperty("NuGetAuditLevel"); + string? auditMode = GetAuditMode(project); HashSet? suppressionItems = GetAuditSuppressions(project.OuterBuild); if (enableAudit != null || auditLevel != null || auditMode != null @@ -304,18 +313,18 @@ private static bool GetPackagePruningDefault(IProject project) // However, that can only be done by an "inner build" evaulation, but we read other audit settings // from the project evaluation, not inner-builds. So, check the inner builds if any TFM sets mode // to "all", otherwise use the project's "outer build" mode. - string GetAuditMode(IProject project) + string? GetAuditMode(IProject project) { foreach (var item in project.TargetFrameworks.NoAllocEnumerate()) { - string auditMode = item.Value.GetProperty("NuGetAuditMode"); + string? auditMode = item.Value.GetProperty("NuGetAuditMode"); if (string.Equals(auditMode, "all", StringComparison.OrdinalIgnoreCase)) { return auditMode; } } - string projectAuditMode = project.OuterBuild.GetProperty("NuGetAuditMode"); + string? projectAuditMode = project.OuterBuild.GetProperty("NuGetAuditMode"); return projectAuditMode; } } @@ -458,7 +467,7 @@ internal static string GetPackagesPath(IProject project, ISettings settings) /// An containing fallback folders to exclude. /// An object containing settings for the project. /// A containing the package fallback folders for the project. - private static string[] GetFallbackFolders(string startupDirectory, string projectDirectory, string[]? fallbackFolders, string[]? fallbackFoldersOverride, IEnumerable additionalProjectFallbackFolders, IEnumerable additionalProjectFallbackFoldersExcludes, ISettings settings) + private static string[] GetFallbackFolders(string? startupDirectory, string projectDirectory, string[]? fallbackFolders, string[]? fallbackFoldersOverride, IEnumerable additionalProjectFallbackFolders, IEnumerable additionalProjectFallbackFoldersExcludes, ISettings settings) { // Fallback folders var currentFallbackFolders = GetValue( @@ -622,17 +631,17 @@ internal static ImmutableArray GetPackageReferences(ITargetFr { var packageReferenceItem = packageReferenceItems[i]; bool autoReferenced = packageReferenceItem.IsMetadataTrue("IsImplicitlyDefined"); - string version = packageReferenceItem.GetMetadata("Version"); + string? version = packageReferenceItem.GetMetadata("Version"); - VersionRange? versionRange = string.IsNullOrWhiteSpace(version) ? null : VersionRange.Parse(version); + VersionRange? versionRange = string.IsNullOrWhiteSpace(version) ? null : VersionRange.Parse(version!); bool versionDefined = versionRange != null; if (versionRange == null && !isCentralPackageVersionManagementEnabled) { versionRange = VersionRange.All; } - string versionOverrideString = packageReferenceItem.GetMetadata("VersionOverride"); - var versionOverrideRange = string.IsNullOrWhiteSpace(versionOverrideString) ? null : VersionRange.Parse(versionOverrideString); + string? versionOverrideString = packageReferenceItem.GetMetadata("VersionOverride"); + var versionOverrideRange = string.IsNullOrWhiteSpace(versionOverrideString) ? null : VersionRange.Parse(versionOverrideString!); CentralPackageVersion? centralPackageVersion = null; bool isCentrallyManaged = !versionDefined && !autoReferenced && isCentralPackageVersionManagementEnabled && versionOverrideRange == null && centralPackageVersions != null && centralPackageVersions.TryGetValue(packageReferenceItem.Identity, out centralPackageVersion); @@ -672,7 +681,7 @@ internal static Dictionary GetPrunePackageReferen foreach (var projectItemInstance in PrunePackageReferences) { string id = projectItemInstance.Identity; - string versionString = projectItemInstance.GetMetadata("Version"); + string? versionString = projectItemInstance.GetMetadata("Version") ?? string.Empty; result.Add(id, PrunePackageReference.Create(id, versionString)); } @@ -692,7 +701,7 @@ internal static IEnumerable GetPackageDownloads(ITargetFrame string id = projectItemInstance.Identity; // PackageDownload items can contain multiple versions - string versionRanges = projectItemInstance.GetMetadata("Version"); + string? versionRanges = projectItemInstance.GetMetadata("Version"); if (string.IsNullOrEmpty(versionRanges)) { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, Strings.Error_PackageDownload_NoVersion, id)); @@ -749,7 +758,7 @@ internal static IEnumerable GetPackageDownloads(ITargetFrame /// A semicolon delimited list of include flags. /// The default value ot return if the value contains no flags. /// The for the specified value, otherwise the . - private static LibraryIncludeFlags GetLibraryIncludeFlags(string value, LibraryIncludeFlags defaultValue) + private static LibraryIncludeFlags GetLibraryIncludeFlags(string? value, LibraryIncludeFlags defaultValue) { if (string.IsNullOrWhiteSpace(value)) { @@ -775,7 +784,7 @@ private static string GetRepositoryPath(IProject project, ISettings settings) () => SettingsUtility.GetRepositoryPath(settings), () => { - string solutionDir = project.OuterBuild.GetProperty("SolutionPath"); + string? solutionDir = project.OuterBuild.GetProperty("SolutionPath"); solutionDir = string.Equals(solutionDir, "*Undefined*", StringComparison.OrdinalIgnoreCase) ? project.Directory @@ -801,8 +810,8 @@ private static Dictionary GetCentralPackageVersio foreach (var projectItemInstance in packageVersionItems) { string id = projectItemInstance.Identity; - string version = projectItemInstance.GetMetadata("Version"); - VersionRange versionRange = string.IsNullOrWhiteSpace(version) ? VersionRange.All : VersionRange.Parse(version); + string? version = projectItemInstance.GetMetadata("Version"); + VersionRange versionRange = string.IsNullOrWhiteSpace(version) ? VersionRange.All : VersionRange.Parse(version!); result.Add(id, new CentralPackageVersion(id, versionRange)); } @@ -829,7 +838,7 @@ internal static List GetSources(IProject project, ISettings setti .ToList(); } - private static string[] GetSources(string startupDirectory, string projectDirectory, string[]? sources, string[]? sourcesOverride, IEnumerable additionalProjectSources, ISettings settings) + private static string[] GetSources(string? startupDirectory, string projectDirectory, string[]? sources, string[]? sourcesOverride, IEnumerable additionalProjectSources, ISettings settings) { // Sources var currentSources = GetValue( @@ -926,7 +935,7 @@ internal static bool IsPropertyTrue(this ITargetFramework project, string proper internal static bool IsPropertyFalse(this ITargetFramework project, string propertyName, bool defaultValue = false) { - string value = project.GetProperty(propertyName); + string? value = project.GetProperty(propertyName); if (string.IsNullOrWhiteSpace(value)) { diff --git a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectFactories.cs b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectFactories.cs index d63136aebc4..ec29b6f8529 100644 --- a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectFactories.cs +++ b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectFactories.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Moq; using NuGet.Commands; +using NuGet.Commands.Restore.Utility; using NuGet.Commands.Test; using NuGet.Frameworks; using NuGet.LibraryModel; @@ -44,7 +45,12 @@ internal static CpsPackageReferenceProject CreateCpsPackageReferenceProject(stri projectId: projectName); } - internal static LegacyPackageReferenceProject CreateLegacyPackageReferenceProject(TestDirectory testDirectory, string projectId, IVsProjectThreadingService threadingService, LibraryDependency[] pkgDependencies) + internal static LegacyPackageReferenceProject CreateLegacyPackageReferenceProject( + TestDirectory testDirectory, + string projectId, + IVsProjectThreadingService threadingService, + LibraryDependency[] pkgDependencies, + bool usePackageSpecFactory) { var framework = NuGetFramework.Parse("netstandard13"); IVsProjectAdapter projectAdapter = CreateProjectAdapter(testDirectory); @@ -54,16 +60,27 @@ internal static LegacyPackageReferenceProject CreateLegacyPackageReferenceProjec framework, pkgDependencies); + var environmentVariables = new TestEnvironmentVariableReader(new Dictionary() + { + { PackageSpecFactory.EnvironmentVariableName, usePackageSpecFactory.ToString() } + }); + var testProject = new LegacyPackageReferenceProject( projectAdapter, projectId, projectServices, - threadingService); + threadingService, + environmentVariables); return testProject; } - internal static LegacyPackageReferenceProject CreateLegacyPackageReferenceProject(TestDirectory testDirectory, string projectId, string range, IVsProjectThreadingService threadingService) + internal static LegacyPackageReferenceProject CreateLegacyPackageReferenceProject( + TestDirectory testDirectory, + string projectId, + string range, + IVsProjectThreadingService threadingService, + bool usePackageSpecFactory) { var onedep = new[] { @@ -76,7 +93,7 @@ internal static LegacyPackageReferenceProject CreateLegacyPackageReferenceProjec } }; - return CreateLegacyPackageReferenceProject(testDirectory: testDirectory, projectId: projectId, threadingService: threadingService, pkgDependencies: onedep); + return CreateLegacyPackageReferenceProject(testDirectory, projectId, threadingService, onedep, usePackageSpecFactory); } internal static IVsProjectAdapter CreateProjectAdapter(string fullPath) 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 dec1602e6fe..b5f1d49d580 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 @@ -15,11 +15,13 @@ using Microsoft.VisualStudio.Threading; using Moq; using NuGet.Commands; +using NuGet.Commands.Restore.Utility; using NuGet.Commands.Test; using NuGet.Common; using NuGet.Configuration; using NuGet.Frameworks; using NuGet.LibraryModel; +using NuGet.PackageManagement.VisualStudio.Projects; using NuGet.Packaging.Core; using NuGet.ProjectManagement; using NuGet.ProjectModel; @@ -49,8 +51,10 @@ public LegacyPackageReferenceProjectTests(GlobalServiceProvider globalServicePro AddService(Task.FromResult((object)componentModel.Object)); } - [Fact] - public async Task GetAssetsFilePathAsync_WithValidMSBuildProjectExtensionsPath_Succeeds() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetAssetsFilePathAsync_WithValidMSBuildProjectExtensionsPath_Succeeds(bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -64,11 +68,12 @@ public async Task GetAssetsFilePathAsync_WithValidMSBuildProjectExtensionsPath_S .Setup(x => x.GetMSBuildProjectExtensionsPath()) .Returns(testMSBuildProjectExtensionsPath); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), new TestProjectSystemServices(), - _threadingService); + _threadingService, + usePackageSpecFactory); // Act var assetsPath = await testProject.GetAssetsFilePathAsync(); @@ -82,19 +87,22 @@ public async Task GetAssetsFilePathAsync_WithValidMSBuildProjectExtensionsPath_S } } - [Fact] - public async Task GetAssetsFilePathAsync_WithNoMSBuildProjectExtensionsPath_Throws() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetAssetsFilePathAsync_WithNoMSBuildProjectExtensionsPath_Throws(bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); // Arrange using (TestDirectory.Create()) { - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( Mock.Of(), Guid.NewGuid().ToString(), new TestProjectSystemServices(), - _threadingService); + _threadingService, + usePackageSpecFactory); // Act & Assert await Assert.ThrowsAsync( @@ -102,8 +110,10 @@ await Assert.ThrowsAsync( } } - [Fact] - public async Task GetCacheFilePathAsync_WithValidMSBuildProjectExtensionsPath_Succeeds() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetCacheFilePathAsync_WithValidMSBuildProjectExtensionsPath_Succeeds(bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -122,11 +132,12 @@ public async Task GetCacheFilePathAsync_WithValidMSBuildProjectExtensionsPath_Su .SetupGet(x => x.FullProjectPath) .Returns(Path.Combine(testDirectory, testProj)); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), new TestProjectSystemServices(), - _threadingService); + _threadingService, + usePackageSpecFactory); // Act var cachePath = await testProject.GetCacheFilePathAsync(); @@ -140,19 +151,22 @@ public async Task GetCacheFilePathAsync_WithValidMSBuildProjectExtensionsPath_Su } } - [Fact] - public async Task GetCacheFilePathAsync_WithNoMSBuildProjectExtensionsPath_Throws() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetCacheFilePathAsync_WithNoMSBuildProjectExtensionsPath_Throws(bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); // Arrange using (TestDirectory.Create()) { - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( Mock.Of(), Guid.NewGuid().ToString(), new TestProjectSystemServices(), - _threadingService); + _threadingService, + usePackageSpecFactory); // Act & Assert await Assert.ThrowsAsync( @@ -160,8 +174,10 @@ await Assert.ThrowsAsync( } } - [Fact] - public async Task GetCacheFilePathAsync_SwitchesToMainThread_Succeeds() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetCacheFilePathAsync_SwitchesToMainThread_Succeeds(bool usePackageSpecFactory) { // Arrange using (var testDirectory = TestDirectory.Create()) @@ -178,11 +194,12 @@ public async Task GetCacheFilePathAsync_SwitchesToMainThread_Succeeds() .SetupGet(x => x.FullProjectPath) .Returns(Path.Combine(testDirectory, testProj)); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), new TestProjectSystemServices(), - _threadingService); + _threadingService, + usePackageSpecFactory); // Act var assetsPath = await testProject.GetCacheFilePathAsync(); @@ -196,8 +213,10 @@ public async Task GetCacheFilePathAsync_SwitchesToMainThread_Succeeds() } } - [Fact] - public async Task GetPackageSpecsAsync_WithDefaultVersion_Succeeds() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageSpecsAsync_WithDefaultVersion_Succeeds(bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -207,11 +226,12 @@ public async Task GetPackageSpecsAsync_WithDefaultVersion_Succeeds() var projectAdapter = CreateProjectAdapter(testDirectory); var projectServices = new TestProjectSystemServices(); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var testDependencyGraphCacheContext = new DependencyGraphCacheContext(); @@ -238,8 +258,10 @@ public async Task GetPackageSpecsAsync_WithDefaultVersion_Succeeds() } } - [Fact] - public async Task GetPackageSpecsAsync_WithVersion_Succeeds() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageSpecsAsync_WithVersion_Succeeds(bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -253,11 +275,12 @@ public async Task GetPackageSpecsAsync_WithVersion_Succeeds() var projectServices = new TestProjectSystemServices(); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var testDependencyGraphCacheContext = new DependencyGraphCacheContext(); @@ -279,12 +302,17 @@ public async Task GetPackageSpecsAsync_WithVersion_Succeeds() } [Theory] - [InlineData("RestorePackagesPath", "Source1;Source2", "Fallback1,Fallback2")] - [InlineData("RestorePackagesPath", "Source2", "Fallback2")] - [InlineData(null, "Source2", "Fallback2")] - [InlineData("RestorePackagesPath", null, "Fallback2")] - [InlineData("RestorePackagesPath", "Source1;Source2", null)] - public async Task GetPackageSpecsAsync_ReadSettingsWithRelativePaths(string? restorePackagesPath, string? sources, string? fallbackFolders) + [InlineData("RestorePackagesPath", "Source1;Source2", "Fallback1,Fallback2", false)] + [InlineData("RestorePackagesPath", "Source2", "Fallback2", false)] + [InlineData(null, "Source2", "Fallback2", false)] + [InlineData("RestorePackagesPath", null, "Fallback2", false)] + [InlineData("RestorePackagesPath", "Source1;Source2", null, false)] + [InlineData("RestorePackagesPath", "Source1;Source2", "Fallback1,Fallback2", true)] + [InlineData("RestorePackagesPath", "Source2", "Fallback2", true)] + [InlineData(null, "Source2", "Fallback2", true)] + [InlineData("RestorePackagesPath", null, "Fallback2", true)] + [InlineData("RestorePackagesPath", "Source1;Source2", null, true)] + public async Task GetPackageSpecsAsync_ReadSettingsWithRelativePaths(string? restorePackagesPath, string? sources, string? fallbackFolders, bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); // Arrange @@ -309,11 +337,12 @@ public async Task GetPackageSpecsAsync_ReadSettingsWithRelativePaths(string? res var projectServices = new TestProjectSystemServices(); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var testDependencyGraphCacheContext = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -345,11 +374,15 @@ public async Task GetPackageSpecsAsync_ReadSettingsWithRelativePaths(string? res } [Theory] - [InlineData(@"C:\RestorePackagesPath", @"C:\Source1;C:\Source2", @"C:\Fallback1;C:\Fallback2")] - [InlineData(null, @"C:\Source1;C:\Source2", @"C:\Fallback1;C:\Fallback2")] - [InlineData(@"C:\RestorePackagesPath", null, @"C:\Fallback1;C:\Fallback2")] - [InlineData(@"C:\RestorePackagesPath", @"C:\Source1;C:\Source2", null)] - public async Task GetPackageSpecsAsync_ReadSettingsWithFullPaths(string? restorePackagesPath, string sources, string fallbackFolders) + [InlineData(@"C:\RestorePackagesPath", @"C:\Source1;C:\Source2", @"C:\Fallback1;C:\Fallback2", false)] + [InlineData(null, @"C:\Source1;C:\Source2", @"C:\Fallback1;C:\Fallback2", false)] + [InlineData(@"C:\RestorePackagesPath", null, @"C:\Fallback1;C:\Fallback2", false)] + [InlineData(@"C:\RestorePackagesPath", @"C:\Source1;C:\Source2", null, false)] + [InlineData(@"C:\RestorePackagesPath", @"C:\Source1;C:\Source2", @"C:\Fallback1;C:\Fallback2", true)] + [InlineData(null, @"C:\Source1;C:\Source2", @"C:\Fallback1;C:\Fallback2", true)] + [InlineData(@"C:\RestorePackagesPath", null, @"C:\Fallback1;C:\Fallback2", true)] + [InlineData(@"C:\RestorePackagesPath", @"C:\Source1;C:\Source2", null, true)] + public async Task GetPackageSpecsAsync_ReadSettingsWithFullPaths(string? restorePackagesPath, string sources, string fallbackFolders, bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -375,11 +408,12 @@ public async Task GetPackageSpecsAsync_ReadSettingsWithFullPaths(string? restore var projectServices = new TestProjectSystemServices(); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var testDependencyGraphCacheContext = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -410,8 +444,10 @@ public async Task GetPackageSpecsAsync_ReadSettingsWithFullPaths(string? restore } } - [Fact] - public async Task GetPackageSpecsAsync_WithPackageTargetFallback_Succeeds() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageSpecsAsync_WithPackageTargetFallback_Succeeds(bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -427,11 +463,12 @@ public async Task GetPackageSpecsAsync_WithPackageTargetFallback_Succeeds() .Returns("portable-net45+win8;dnxcore50"); #pragma warning restore CS0618 // Type or member is obsolete - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), new TestProjectSystemServices(), - _threadingService); + _threadingService, + usePackageSpecFactory); var testDependencyGraphCacheContext = new DependencyGraphCacheContext(); @@ -467,8 +504,10 @@ public async Task GetPackageSpecsAsync_WithPackageTargetFallback_Succeeds() } } - [Fact] - public async Task GetPackageSpecsAsync_WithPackageReference_Succeeds() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageSpecsAsync_WithPackageReference_Succeeds(bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -490,11 +529,12 @@ public async Task GetPackageSpecsAsync_WithPackageReference_Succeeds() LibraryDependencyTarget.Package) }); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var testDependencyGraphCacheContext = new DependencyGraphCacheContext(); @@ -520,8 +560,10 @@ public async Task GetPackageSpecsAsync_WithPackageReference_Succeeds() } } - [Fact] - public async Task GetPackageSpecsAsync_WithProjectReference_Succeeds() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageSpecsAsync_WithProjectReference_Succeeds(bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -540,11 +582,12 @@ public async Task GetPackageSpecsAsync_WithProjectReference_Succeeds() ProjectPath = Path.Combine(randomTestFolder, "TestProjectA") }); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var testDependencyGraphCacheContext = new DependencyGraphCacheContext(); @@ -569,8 +612,10 @@ public async Task GetPackageSpecsAsync_WithProjectReference_Succeeds() } } - [Fact] - public async Task GetInstalledPackagesAsync_WhenValid_ReturnsPackageReferences() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetInstalledPackagesAsync_WhenValid_ReturnsPackageReferences(bool usePackageSpecFactory) { // Arrange using (var randomTestFolder = TestDirectory.Create()) @@ -590,11 +635,12 @@ public async Task GetInstalledPackagesAsync_WhenValid_ReturnsPackageReferences() LibraryDependencyTarget.Package) }); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); // Act var packageReferences = await testProject.GetInstalledPackagesAsync(CancellationToken.None); @@ -614,8 +660,10 @@ public async Task GetInstalledPackagesAsync_WhenValid_ReturnsPackageReferences() } } - [Fact] - public async Task InstallPackageAsync_AddsPackageReference() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task InstallPackageAsync_AddsPackageReference(bool usePackageSpecFactory) { // Arrange using (var randomTestFolder = TestDirectory.Create()) @@ -631,11 +679,12 @@ public async Task InstallPackageAsync_AddsPackageReference() .Callback((d, _) => actualDependency = d) .Returns(Task.CompletedTask); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var buildIntegratedInstallationContext = new BuildIntegratedInstallationContext() { @@ -667,8 +716,10 @@ public async Task InstallPackageAsync_AddsPackageReference() } } - [Fact] - public async Task UninstallPackageAsync_Always_RemovesPackageReference() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task UninstallPackageAsync_Always_RemovesPackageReference(bool usePackageSpecFactory) { // Arrange using (var randomTestFolder = TestDirectory.Create()) @@ -683,11 +734,12 @@ public async Task UninstallPackageAsync_Always_RemovesPackageReference() .Callback(p => actualPackageId = p) .Returns(Task.CompletedTask); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); // Act var result = await testProject.UninstallPackageAsync( @@ -707,8 +759,10 @@ public async Task UninstallPackageAsync_Always_RemovesPackageReference() } } - [Fact] - public async Task GetPackageSpecsAsync_SkipContentFilesAlwaysTrue() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageSpecsAsync_SkipContentFilesAlwaysTrue(bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -730,11 +784,12 @@ public async Task GetPackageSpecsAsync_SkipContentFilesAlwaysTrue() LibraryDependencyTarget.Package) }); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var testDependencyGraphCacheContext = new DependencyGraphCacheContext(); @@ -752,14 +807,19 @@ public async Task GetPackageSpecsAsync_SkipContentFilesAlwaysTrue() } [Theory] - [InlineData("true", null, false)] - [InlineData(null, "packages.A.lock.json", false)] - [InlineData("true", null, true)] - [InlineData("false", null, false)] + [InlineData("true", null, false, false)] + [InlineData(null, "packages.A.lock.json", false, false)] + [InlineData("true", null, true, false)] + [InlineData("false", null, false, false)] + [InlineData("true", null, false, true)] + [InlineData(null, "packages.A.lock.json", false, true)] + [InlineData("true", null, true, true)] + [InlineData("false", null, false, true)] public async Task GetPackageSpecsAsync_ReadLockFileSettings( string? restorePackagesWithLockFile, string? lockFilePath, - bool restoreLockedMode) + bool restoreLockedMode, + bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -785,11 +845,12 @@ public async Task GetPackageSpecsAsync_ReadLockFileSettings( var projectServices = new TestProjectSystemServices(); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var testDependencyGraphCacheContext = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -813,8 +874,10 @@ public async Task GetPackageSpecsAsync_ReadLockFileSettings( } } - [Fact] - public async Task GetPackageSpecAsync_CentralPackageVersionsRemovedDuplicates() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageSpecAsync_CentralPackageVersionsRemovedDuplicates(bool usePackageSpecFactory) { // Arrange var packageAv1 = (PackageId: "packageA", Version: "1.2.3"); @@ -837,11 +900,12 @@ public async Task GetPackageSpecAsync_CentralPackageVersionsRemovedDuplicates() restoreLockedMode: false, projectPackageVersions: new List<(string Id, string Version)>() { packageAv1, packageB, packageAv5 }); - var legacyPRProject = new LegacyPackageReferenceProject( + var legacyPRProject = CreateLegacyPackageReferenceProject( vsProjectAdapter, Guid.NewGuid().ToString(), new TestProjectSystemServices(), - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -858,15 +922,23 @@ public async Task GetPackageSpecAsync_CentralPackageVersionsRemovedDuplicates() } [Theory] - [InlineData(null, true)] - [InlineData("", true)] - [InlineData(" ", true)] - [InlineData("true", true)] - [InlineData("invalid", true)] - [InlineData("false", false)] - [InlineData(" false ", false)] - [InlineData("FaLsE", false)] - public async Task GetPackageSpecAsync_CentralPackageVersionOverride_DisabedWhenSpecified(string? isCentralPackageVersionOverrideEnabled, bool expected) + [InlineData(null, true, false)] + [InlineData("", true, false)] + [InlineData(" ", true, false)] + [InlineData("true", true, false)] + [InlineData("invalid", true, false)] + [InlineData("false", false, false)] + [InlineData(" false ", false, false)] + [InlineData("FaLsE", false, false)] + [InlineData(null, true, true)] + [InlineData("", true, true)] + [InlineData(" ", true, true)] + [InlineData("true", true, true)] + [InlineData("invalid", true, true)] + [InlineData("false", false, true)] + [InlineData(" false ", false, true)] + [InlineData("FaLsE", false, true)] + public async Task GetPackageSpecAsync_CentralPackageVersionOverride_DisabedWhenSpecified(string? isCentralPackageVersionOverrideEnabled, bool expected, bool usePackageSpecFactory) { // Arrange var packageAv1 = (PackageId: "packageA", Version: "1.2.3"); @@ -890,11 +962,12 @@ public async Task GetPackageSpecAsync_CentralPackageVersionOverride_DisabedWhenS projectPackageVersions: new List<(string Id, string Version)>() { packageAv1, packageB, packageAv5 }, isCentralPackageVersionOverrideEnabled: isCentralPackageVersionOverrideEnabled); - var legacyPRProject = new LegacyPackageReferenceProject( + var legacyPRProject = CreateLegacyPackageReferenceProject( vsProjectAdapter, Guid.NewGuid().ToString(), new TestProjectSystemServices(), - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -914,16 +987,25 @@ public async Task GetPackageSpecAsync_CentralPackageVersionOverride_DisabedWhenS } [Theory] - [InlineData(null, false)] - [InlineData("", false)] - [InlineData(" ", false)] - [InlineData("invalid", false)] - [InlineData("false", false)] - [InlineData(" false ", false)] - [InlineData("FaLsE", false)] - [InlineData("true", true)] - [InlineData(" true ", true)] - public async Task GetPackageSpecAsync_TransitiveDependencyPinning_CanBeEnabled(string? transitiveDependencyPinning, bool expected) + [InlineData(null, false, false)] + [InlineData("", false, false)] + [InlineData(" ", false, false)] + [InlineData("invalid", false, false)] + [InlineData("false", false, false)] + [InlineData(" false ", false, false)] + [InlineData("FaLsE", false, false)] + [InlineData("true", true, false)] + [InlineData(" true ", true, false)] + [InlineData(null, false, true)] + [InlineData("", false, true)] + [InlineData(" ", false, true)] + [InlineData("invalid", false, true)] + [InlineData("false", false, true)] + [InlineData(" false ", false, true)] + [InlineData("FaLsE", false, true)] + [InlineData("true", true, true)] + [InlineData(" true ", true, true)] + public async Task GetPackageSpecAsync_TransitiveDependencyPinning_CanBeEnabled(string? transitiveDependencyPinning, bool expected, bool usePackageSpecFactory) { // Arrange var projectNames = new ProjectNames( @@ -943,11 +1025,12 @@ public async Task GetPackageSpecAsync_TransitiveDependencyPinning_CanBeEnabled(s projectPackageVersions: new List<(string Id, string Version)>() { }, CentralPackageTransitivePinningEnabled: transitiveDependencyPinning); - var legacyPRProject = new LegacyPackageReferenceProject( + var legacyPRProject = CreateLegacyPackageReferenceProject( vsProjectAdapter, Guid.NewGuid().ToString(), new TestProjectSystemServices(), - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -966,13 +1049,15 @@ public async Task GetPackageSpecAsync_TransitiveDependencyPinning_CanBeEnabled(s } } - [Fact] - public async Task GetInstalledVersion_WithAssetsFile_ReturnsVersionsFromAssetsSpecs() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetInstalledVersion_WithAssetsFile_ReturnsVersionsFromAssetsSpecs(bool usePackageSpecFactory) { using (var testDirectory = TestDirectory.Create()) { // Setup - LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[1.0.0, )"); + LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[1.0.0, )", usePackageSpecFactory); var settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1008,13 +1093,15 @@ public async Task GetInstalledVersion_WithAssetsFile_ReturnsVersionsFromAssetsSp } } - [Fact] - public async Task GetInstalledVersion_WithAssetsFile_ReturnsVersionsFromAssetsSpecs_ValidateCache() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetInstalledVersion_WithAssetsFile_ReturnsVersionsFromAssetsSpecs_ValidateCache(bool usePackageSpecFactory) { using (var testDirectory = TestDirectory.Create()) { // Setup - LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[1.0.0, )"); + LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[1.0.0, )", usePackageSpecFactory); var settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1053,13 +1140,15 @@ public async Task GetInstalledVersion_WithAssetsFile_ReturnsVersionsFromAssetsSp } } - [Fact] - public async Task GetInstalledVersion_WithFloating_WithAssetsFile_ReturnsVersionsFromAssetsSpecs() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetInstalledVersion_WithFloating_WithAssetsFile_ReturnsVersionsFromAssetsSpecs(bool usePackageSpecFactory) { using (var testDirectory = TestDirectory.Create()) { // Setup - LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[2.*, )"); + LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[2.*, )", usePackageSpecFactory); var settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1098,13 +1187,15 @@ public async Task GetInstalledVersion_WithFloating_WithAssetsFile_ReturnsVersion } } - [Fact] - public async Task GetInstalledVersion_WithoutAssetsFile_ReturnsVersionsFromPackageSpecs() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetInstalledVersion_WithoutAssetsFile_ReturnsVersionsFromPackageSpecs(bool usePackageSpecFactory) { using (var testDirectory = TestDirectory.Create()) { // Setup - LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[2.0.0, )"); + LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[2.0.0, )", usePackageSpecFactory); var settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1134,13 +1225,15 @@ public async Task GetInstalledVersion_WithoutAssetsFile_ReturnsVersionsFromPacka } } - [Fact] - public async Task GetInstalledVersion_WithoutPackages_ReturnsEmpty() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetInstalledVersion_WithoutPackages_ReturnsEmpty(bool usePackageSpecFactory) { using (var testDirectory = TestDirectory.Create()) { // Setup - LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProjectNoPackages(testDirectory); + LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProjectNoPackages(testDirectory, usePackageSpecFactory); var settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1169,13 +1262,15 @@ public async Task GetInstalledVersion_WithoutPackages_ReturnsEmpty() } } - [Fact] - public async Task GetInstalledVersion_WithAssetsFile_ReturnsVsersionsFromAssets() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetInstalledVersion_WithAssetsFile_ReturnsVsersionsFromAssets(bool usePackageSpecFactory) { using (var testDirectory = TestDirectory.Create()) { // Setup - LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[2.0.0, )"); + LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[2.0.0, )", usePackageSpecFactory); var settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1211,13 +1306,15 @@ public async Task GetInstalledVersion_WithAssetsFile_ReturnsVsersionsFromAssets( } } - [Fact] - public async Task GetInstalledVersion_WithoutPackages_WithAssets_ReturnsEmpty() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetInstalledVersion_WithoutPackages_WithAssets_ReturnsEmpty(bool usePackageSpecFactory) { using (var testDirectory = TestDirectory.Create()) { // Setup - LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProjectNoPackages(testDirectory); + LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProjectNoPackages(testDirectory, usePackageSpecFactory); var settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1252,13 +1349,15 @@ public async Task GetInstalledVersion_WithoutPackages_WithAssets_ReturnsEmpty() } } - [Fact] - public async Task GetTransitivePackagesAsync_WithTransitivePackageReferences_ReturnsPackageIdentities() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetTransitivePackagesAsync_WithTransitivePackageReferences_ReturnsPackageIdentities(bool usePackageSpecFactory) { using (TestDirectory testDirectory = TestDirectory.Create()) { // Setup - LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[1.0.0, )"); + LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[1.0.0, )", usePackageSpecFactory); NullSettings settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1303,13 +1402,15 @@ await SimpleTestPackageUtility.CreateFullPackageAsync( } } - [Fact] - public async Task GetTransitivePackagesAsync_WithNestedTransitivePackageReferences_ReturnsPackageIdentities() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetTransitivePackagesAsync_WithNestedTransitivePackageReferences_ReturnsPackageIdentities(bool usePackageSpecFactory) { using (TestDirectory testDirectory = TestDirectory.Create()) { // Setup - LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[1.0.0, )"); + LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[1.0.0, )", usePackageSpecFactory); NullSettings settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1363,13 +1464,15 @@ await SimpleTestPackageUtility.CreateFullPackageAsync( } } - [Fact] - public async Task GetTransitivePackagesAsync_WithNoTransitivePackageReferences_ReturnsOnlyInstalledPackageIdentities() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetTransitivePackagesAsync_WithNoTransitivePackageReferences_ReturnsOnlyInstalledPackageIdentities(bool usePackageSpecFactory) { using (TestDirectory testDirectory = TestDirectory.Create()) { // Setup - LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[1.0.0, )"); + LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[1.0.0, )", usePackageSpecFactory); NullSettings settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1406,13 +1509,15 @@ public async Task GetTransitivePackagesAsync_WithNoTransitivePackageReferences_R } } - [Fact] - public async Task GetTransitivePackagesAsync_WithTransitivePackageReferences_ReturnsPackageIdentitiesFromCache() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetTransitivePackagesAsync_WithTransitivePackageReferences_ReturnsPackageIdentitiesFromCache(bool usePackageSpecFactory) { using (TestDirectory testDirectory = TestDirectory.Create()) { // Setup - LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[1.0.0, )"); + LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, "[1.0.0, )", usePackageSpecFactory); NullSettings settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1463,12 +1568,23 @@ await SimpleTestPackageUtility.CreateFullPackageAsync( } [Theory] - [InlineData(null, null, null, 0, 0)] - [InlineData("win-x64", null, null, 1, 0)] - [InlineData("win-x64", "win-x86", null, 2, 0)] - [InlineData("win-x64", "win-x86;win-x64", null, 2, 0)] - [InlineData("win-x64", "win-x86;win-x64", "win", 2, 1)] - public async Task GetPackageSpecsAsync_WithRuntimeIdentifiers_GeneratesRuntimeGraph(string? runtimeIdentifier, string? runtimeIdentifiers, string? runtimeSupports, int runtimeCount, int supportsCount) + [InlineData(null, null, null, 0, 0, false)] + [InlineData("win-x64", null, null, 1, 0, false)] + [InlineData("win-x64", "win-x86", null, 2, 0, false)] + [InlineData("win-x64", "win-x86;win-x64", null, 2, 0, false)] + [InlineData("win-x64", "win-x86;win-x64", "win", 2, 1, false)] + [InlineData(null, null, null, 0, 0, true)] + [InlineData("win-x64", null, null, 1, 0, true)] + [InlineData("win-x64", "win-x86", null, 2, 0, true)] + [InlineData("win-x64", "win-x86;win-x64", null, 2, 0, true)] + [InlineData("win-x64", "win-x86;win-x64", "win", 2, 1, true)] + public async Task GetPackageSpecsAsync_WithRuntimeIdentifiers_GeneratesRuntimeGraph( + string? runtimeIdentifier, + string? runtimeIdentifiers, + string? runtimeSupports, + int runtimeCount, + int supportsCount, + bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); // Arrange @@ -1493,11 +1609,12 @@ public async Task GetPackageSpecsAsync_WithRuntimeIdentifiers_GeneratesRuntimeGr var projectServices = new TestProjectSystemServices(); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var testDependencyGraphCacheContext = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1541,8 +1658,10 @@ public void GetRuntimeSupports_WithVariousInputs(string? runtimeSupports, string Assert.Equal(expected, string.Join(";", actual.Select(e => e.Name.ToString()))); } - [Fact] - public async Task GetPackageSpecs_WithWarningProperties() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageSpecs_WithWarningProperties(bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); // Arrange @@ -1568,11 +1687,12 @@ public async Task GetPackageSpecs_WithWarningProperties() var projectServices = new TestProjectSystemServices(); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var testDependencyGraphCacheContext = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1597,8 +1717,10 @@ public async Task GetPackageSpecs_WithWarningProperties() projectBuildProperties.VerifyAll(); } - [Fact] - public async Task GetPackageSpec_WithNuGetAuditSuppress() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageSpec_WithNuGetAuditSuppress(bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -1613,11 +1735,12 @@ public async Task GetPackageSpec_WithNuGetAuditSuppress() .Returns([("https://cve.test/1", Array.Empty())]); var projectServices = new TestProjectSystemServices(); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var testDependencyGraphCacheContext = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1635,8 +1758,10 @@ public async Task GetPackageSpec_WithNuGetAuditSuppress() auditProperties.SuppressedAdvisories.Should().BeEquivalentTo(["https://cve.test/1"]); } - [Fact] - public async Task GetPackageSpec_WithValidSdkAnalysisLevel_ReadsSdkAnalysisLevelValue() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageSpec_WithValidSdkAnalysisLevel_ReadsSdkAnalysisLevelValue(bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -1652,11 +1777,12 @@ public async Task GetPackageSpec_WithValidSdkAnalysisLevel_ReadsSdkAnalysisLevel Mock projectAdapterMock = Mock.Get(projectAdapter); var projectServices = new TestProjectSystemServices(); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var testDependencyGraphCacheContext = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1672,10 +1798,13 @@ public async Task GetPackageSpec_WithValidSdkAnalysisLevel_ReadsSdkAnalysisLevel } [Theory] - [InlineData("False")] - [InlineData("FaLse")] - [InlineData("false")] - public async Task GetPackageSpec_WithFalseUsingMicrosoftNetSdk_ReadsFalse(string usingSdk) + [InlineData("False", false)] + [InlineData("FaLse", false)] + [InlineData("false", false)] + [InlineData("False", true)] + [InlineData("FaLse", true)] + [InlineData("false", true)] + public async Task GetPackageSpec_WithFalseUsingMicrosoftNetSdk_ReadsFalse(string usingSdk, bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -1689,11 +1818,12 @@ public async Task GetPackageSpec_WithFalseUsingMicrosoftNetSdk_ReadsFalse(string Mock projectAdapterMock = Mock.Get(projectAdapter); var projectServices = new TestProjectSystemServices(); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var testDependencyGraphCacheContext = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1710,10 +1840,13 @@ public async Task GetPackageSpec_WithFalseUsingMicrosoftNetSdk_ReadsFalse(string } [Theory] - [InlineData("True")] - [InlineData("true")] - [InlineData("TrUe")] - public async Task GetPackageSpec_WithTrueUsingMicrosoftNetSdk_ReadsTrue(string usingSdk) + [InlineData("True", false)] + [InlineData("true", false)] + [InlineData("TrUe", false)] + [InlineData("True", true)] + [InlineData("true", true)] + [InlineData("TrUe", true)] + public async Task GetPackageSpec_WithTrueUsingMicrosoftNetSdk_ReadsTrue(string usingSdk, bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -1727,11 +1860,12 @@ public async Task GetPackageSpec_WithTrueUsingMicrosoftNetSdk_ReadsTrue(string u Mock projectAdapterMock = Mock.Get(projectAdapter); var projectServices = new TestProjectSystemServices(); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var testDependencyGraphCacheContext = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1747,8 +1881,10 @@ public async Task GetPackageSpec_WithTrueUsingMicrosoftNetSdk_ReadsTrue(string u Assert.True(actualRestoreSpec.RestoreMetadata.UsingMicrosoftNETSdk); } - [Fact] - public async Task GetPackageSpec_WithInvalidSdkAnalysisLevel_ThrowsAnException() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageSpec_WithInvalidSdkAnalysisLevel_ThrowsAnException(bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -1762,11 +1898,12 @@ public async Task GetPackageSpec_WithInvalidSdkAnalysisLevel_ThrowsAnException() Mock projectAdapterMock = Mock.Get(projectAdapter); var projectServices = new TestProjectSystemServices(); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var testDependencyGraphCacheContext = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1775,8 +1912,10 @@ public async Task GetPackageSpec_WithInvalidSdkAnalysisLevel_ThrowsAnException() await Assert.ThrowsAsync(async () => await testProject.GetPackageSpecsAsync(testDependencyGraphCacheContext)); } - [Fact] - public async Task GetPackageSpec_WithInvalidUsingMicrosoftNetSdk_ThrowsAnException() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageSpec_WithInvalidUsingMicrosoftNetSdk_ThrowsAnException(bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -1790,11 +1929,12 @@ public async Task GetPackageSpec_WithInvalidUsingMicrosoftNetSdk_ThrowsAnExcepti Mock projectAdapterMock = Mock.Get(projectAdapter); var projectServices = new TestProjectSystemServices(); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var testDependencyGraphCacheContext = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1804,10 +1944,13 @@ public async Task GetPackageSpec_WithInvalidUsingMicrosoftNetSdk_ThrowsAnExcepti } [Theory] - [InlineData("False", false)] - [InlineData("true", true)] - [InlineData(null, false)] - public async Task GetPackageSpec_WithUseLegacyDependencyResolver(string? restoreUseLegacyDependencyResolver, bool expected) + [InlineData("False", false, false)] + [InlineData("true", true, false)] + [InlineData(null, false, false)] + [InlineData("False", false, true)] + [InlineData("true", true, true)] + [InlineData(null, false, true)] + public async Task GetPackageSpec_WithUseLegacyDependencyResolver(string? restoreUseLegacyDependencyResolver, bool expected, bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); @@ -1818,11 +1961,12 @@ public async Task GetPackageSpec_WithUseLegacyDependencyResolver(string? restore .Returns(restoreUseLegacyDependencyResolver); var projectAdapter = CreateProjectAdapter(testDirectory, projectBuildProperties); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), new TestProjectSystemServices(), - _threadingService); + _threadingService, + usePackageSpecFactory); // Act var packageSpecs = await testProject.GetPackageSpecsAsync(new DependencyGraphCacheContext(NullLogger.Instance, NullSettings.Instance)); @@ -1835,8 +1979,10 @@ public async Task GetPackageSpec_WithUseLegacyDependencyResolver(string? restore actualRestoreSpec.RestoreMetadata.UseLegacyDependencyResolver.Should().Be(expected); } - [Fact] - public async Task GetPackageSpecAsync_WithVariousCentralPackageVersions_AppliesFlagsCorreclty() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageSpecAsync_WithVariousCentralPackageVersions_AppliesFlagsCorreclty(bool usePackageSpecFactory) { // Arrange var tfm = NuGetFramework.Parse("net472"); @@ -1896,11 +2042,12 @@ public async Task GetPackageSpecAsync_WithVariousCentralPackageVersions_AppliesF projectPackageVersions: new List<(string Id, string Version)>() { packageA, packageB, packageC, packageD }, isCentralPackageVersionOverrideEnabled: "true"); - var legacyPRProject = new LegacyPackageReferenceProject( + var legacyPRProject = CreateLegacyPackageReferenceProject( vsProjectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1940,8 +2087,10 @@ public async Task GetPackageSpecAsync_WithVariousCentralPackageVersions_AppliesF tfi.Dependencies[3].VersionOverride.Should().Be(VersionRange.Parse("3.0.0")); } - [Fact] - public async Task GetPackageSpecAsync_WithDifferentCasingPackageVersionAndPackageReference_CombinesCorrectly() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageSpecAsync_WithDifferentCasingPackageVersionAndPackageReference_CombinesCorrectly(bool usePackageSpecFactory) { // Arrange var tfm = NuGetFramework.Parse("net472"); @@ -1976,11 +2125,12 @@ public async Task GetPackageSpecAsync_WithDifferentCasingPackageVersionAndPackag projectPackageVersions: new List<(string Id, string Version)>() { packageAUpperCase }, isCentralPackageVersionOverrideEnabled: "true"); - var legacyPRProject = new LegacyPackageReferenceProject( + var legacyPRProject = CreateLegacyPackageReferenceProject( vsProjectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -2004,17 +2154,23 @@ public async Task GetPackageSpecAsync_WithDifferentCasingPackageVersionAndPackag public static readonly List PrunePackageReferenceData = new List { - new object[] { "true", new (string, string[])[] { ("PackageA", ["1.0.0"]) }, true }, - new object[] { "false", new (string, string[])[] { ("PackageA", ["1.0.0"]) }, false }, + new object[] { "true", new (string, string[])[] { ("PackageA", ["1.0.0"]) }, true, true }, + new object[] { "false", new (string, string[])[] { ("PackageA", ["1.0.0"]) }, false, true }, + new object[] { "true", new (string, string[])[] { ("PackageA", ["1.0.0"]) }, true, false }, + new object[] { "false", new (string, string[])[] { ("PackageA", ["1.0.0"]) }, false, false }, }; [Theory] [MemberData(nameof(PrunePackageReferenceData))] - public async Task GetPackageSpec_WithPrunePackageReferences(string restoreEnablePackagePruning, IEnumerable<(string ItemId, string[] ItemMetadata)> buildIteminfo, bool hasPrunedReferences) + public async Task GetPackageSpec_WithPrunePackageReferences( + string restoreEnablePackagePruning, + IEnumerable<(string ItemId, string[] ItemMetadata)> buildIteminfo, + bool hasPrunedReferences, + bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); using var testDirectory = TestDirectory.Create(); - IReadOnlyList packageSpecs = await SetupPrunePackageReferenceDataAndAct(restoreEnablePackagePruning, buildIteminfo, testDirectory); + IReadOnlyList packageSpecs = await SetupPrunePackageReferenceDataAndAct(restoreEnablePackagePruning, buildIteminfo, testDirectory, usePackageSpecFactory); // Assert Assert.NotNull(packageSpecs); @@ -2036,16 +2192,22 @@ public async Task GetPackageSpec_WithPrunePackageReferences(string restoreEnable } } - [Fact] - public async Task GetPackageSpec_WithPrunePackageReferenceAndMissingVersion_Throws() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageSpec_WithPrunePackageReferenceAndMissingVersion_Throws(bool usePackageSpecFactory) { await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); using var testDirectory = TestDirectory.Create(); - var exception = await Assert.ThrowsAsync(() => SetupPrunePackageReferenceDataAndAct("true", new (string, string[])[] { ("PackageA", [null!]) }, testDirectory)); + var exception = await Assert.ThrowsAsync(() => SetupPrunePackageReferenceDataAndAct("true", new (string, string[])[] { ("PackageA", [null!]) }, testDirectory, usePackageSpecFactory)); exception.Message.Should().Contain("PrunePackageReference"); } - private async Task> SetupPrunePackageReferenceDataAndAct(string restoreEnablePackagePruning, IEnumerable<(string ItemId, string[] ItemMetadata)> buildIteminfo, TestDirectory testDirectory) + private async Task> SetupPrunePackageReferenceDataAndAct( + string restoreEnablePackagePruning, + IEnumerable<(string ItemId, string[] ItemMetadata)> buildIteminfo, + TestDirectory testDirectory, + bool usePackageSpecFactory) { // Arrange var projectBuildProperties = new Mock(); @@ -2057,11 +2219,12 @@ private async Task> SetupPrunePackageReferenceDataAnd projectAdapterMock.Setup(m => m.GetBuildItemInformation(ProjectItems.PrunePackageReference, It.IsAny())) .Returns(buildIteminfo); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), new TestProjectSystemServices(), - _threadingService); + _threadingService, + usePackageSpecFactory); var settings = NullSettings.Instance; var testDependencyGraphCacheContext = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -2070,22 +2233,45 @@ private async Task> SetupPrunePackageReferenceDataAnd return await testProject.GetPackageSpecsAsync(testDependencyGraphCacheContext); } - private LegacyPackageReferenceProject CreateLegacyPackageReferenceProject(TestDirectory testDirectory, string range) + private LegacyPackageReferenceProject CreateLegacyPackageReferenceProject(TestDirectory testDirectory, string range, bool usePackageSpecFactory) { - return ProjectFactories.CreateLegacyPackageReferenceProject(testDirectory, Guid.NewGuid().ToString(), range, _threadingService); + return ProjectFactories.CreateLegacyPackageReferenceProject(testDirectory, Guid.NewGuid().ToString(), range, _threadingService, usePackageSpecFactory); } - private LegacyPackageReferenceProject CreateLegacyPackageReferenceProjectNoPackages(TestDirectory testDirectory) + private LegacyPackageReferenceProject CreateLegacyPackageReferenceProjectNoPackages(TestDirectory testDirectory, bool usePackageSpecFactory) { var projectAdapter = CreateProjectAdapter(testDirectory); var projectServices = new TestProjectSystemServices(); - var testProject = new LegacyPackageReferenceProject( + var testProject = CreateLegacyPackageReferenceProject( projectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); + return testProject; + } + + private LegacyPackageReferenceProject CreateLegacyPackageReferenceProject( + IVsProjectAdapter projectAdapter, + string projectId, + ILegacyPackageReferenceProjectServices projectServices, + IVsProjectThreadingService threadingService, + bool usePackageSpecFactory) + { + var environmentVariables = new TestEnvironmentVariableReader(new Dictionary() + { + { PackageSpecFactory.EnvironmentVariableName, usePackageSpecFactory.ToString() } + }); + + var testProject = new LegacyPackageReferenceProject( + projectAdapter, + projectId, + projectServices, + threadingService, + environmentVariables); + return testProject; } } diff --git a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceRestoreUtilityTests.cs b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceRestoreUtilityTests.cs index 7bdf1d7d7a1..87c3ad4208e 100644 --- a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceRestoreUtilityTests.cs +++ b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceRestoreUtilityTests.cs @@ -11,10 +11,12 @@ using System.Threading.Tasks; using Microsoft.VisualStudio.Sdk.TestFramework; using NuGet.Commands; +using NuGet.Commands.Restore.Utility; using NuGet.Common; using NuGet.Configuration; using NuGet.Frameworks; using NuGet.LibraryModel; +using NuGet.PackageManagement.VisualStudio.Projects; using NuGet.Packaging.Core; using NuGet.ProjectManagement; using NuGet.ProjectModel; @@ -43,8 +45,10 @@ public LegacyPackageReferenceRestoreUtilityTests(GlobalServiceProvider globalSer _threadingService = new TestProjectThreadingService(NuGetUIThreadHelper.JoinableTaskFactory); } - [Fact] - public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Success() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Success(bool usePackageSpecFactory) { using (var packageSource = TestDirectory.Create()) { @@ -90,11 +94,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Success LibraryDependencyTarget.Package) }); - var legacyPRProject = new LegacyPackageReferenceProject( + var legacyPRProject = CreateLegacyPackageReferenceProject( vsProjectAdapter, projectNames.ProjectId, projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProject); var testLogger = new TestLogger(); @@ -132,8 +137,10 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Success } } - [Fact] - public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_GenerateLockFile() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_GenerateLockFile(bool usePackageSpecFactory) { using (var packageSource = TestDirectory.Create()) { @@ -180,11 +187,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Generat LibraryDependencyTarget.Package) }); - var legacyPRProjectB = new LegacyPackageReferenceProject( + var legacyPRProjectB = CreateLegacyPackageReferenceProject( vsProjectAdapterB, Guid.NewGuid().ToString(), projectServicesB, - _threadingService); + _threadingService, + usePackageSpecFactory); var projectPathA = Path.Combine(testSolutionManager.TestDirectory, "ProjectA"); var fullProjectPathA = Path.Combine(projectPathA, "Project1.csproj"); @@ -217,11 +225,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Generat ProjectPath = fullProjectPathB }); - var legacyPRProjectA = new LegacyPackageReferenceProject( + var legacyPRProjectA = CreateLegacyPackageReferenceProject( vsProjectAdapterA, Guid.NewGuid().ToString(), projectServicesA, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProjectB); testSolutionManager.NuGetProjects.Add(legacyPRProjectA); @@ -275,8 +284,10 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Generat } } - [Fact] - public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_ReadLockFile() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_ReadLockFile(bool usePackageSpecFactory) { using (var packageSource = TestDirectory.Create()) { @@ -322,11 +333,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_ReadLoc LibraryDependencyTarget.Package) }); - var legacyPRProject = new LegacyPackageReferenceProject( + var legacyPRProject = CreateLegacyPackageReferenceProject( vsProjectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProject); var testLogger = new TestLogger(); @@ -403,8 +415,10 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_ReadLoc } } - [Fact] - public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_UpdateLockFile() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_UpdateLockFile(bool usePackageSpecFactory) { using (var packageSource = TestDirectory.Create()) { @@ -455,11 +469,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_UpdateL LibraryDependencyTarget.Package) }); - var legacyPRProject = new LegacyPackageReferenceProject( + var legacyPRProject = CreateLegacyPackageReferenceProject( vsProjectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProject); var testLogger = new TestLogger(); @@ -558,8 +573,10 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_UpdateL } } - [Fact] - public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_RestorePackagesWithLockFile_False() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_RestorePackagesWithLockFile_False(bool usePackageSpecFactory) { using (var packageSource = TestDirectory.Create()) { @@ -609,11 +626,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Restore LibraryDependencyTarget.Package) }); - var legacyPRProject = new LegacyPackageReferenceProject( + var legacyPRProject = CreateLegacyPackageReferenceProject( vsProjectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProject); var testLogger = new TestLogger(); @@ -649,8 +667,10 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Restore } } - [Fact] - public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_LockedMode() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_LockedMode(bool usePackageSpecFactory) { using (var packageSource = TestDirectory.Create()) { @@ -699,11 +719,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_LockedM LibraryDependencyTarget.Package) }); - var legacyPRProject = new LegacyPackageReferenceProject( + var legacyPRProject = CreateLegacyPackageReferenceProject( vsProjectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProject); var testLogger = new TestLogger(); @@ -784,8 +805,10 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_LockedM } } - [Fact] - public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_PackageShaValidationFailed() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_PackageShaValidationFailed(bool usePackageSpecFactory) { using (var packageSource = TestDirectory.Create()) { @@ -831,11 +854,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Package LibraryDependencyTarget.Package) }); - var legacyPRProject = new LegacyPackageReferenceProject( + var legacyPRProject = CreateLegacyPackageReferenceProject( vsProjectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProject); var testLogger = new TestLogger(); @@ -910,8 +934,10 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Package } } - [Fact] - public async Task LegacyPackageReference_Restore_PackageShaValidationFailed_LogsAllPackageIds() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task LegacyPackageReference_Restore_PackageShaValidationFailed_LogsAllPackageIds(bool usePackageSpecFactory) { using (var packageSource = TestDirectory.Create()) { @@ -962,11 +988,12 @@ public async Task LegacyPackageReference_Restore_PackageShaValidationFailed_Logs LibraryDependencyTarget.Package) }); - var legacyPRProject = new LegacyPackageReferenceProject( + var legacyPRProject = CreateLegacyPackageReferenceProject( vsProjectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProject); var testLogger = new TestLogger(); @@ -1066,8 +1093,10 @@ private static string GetPackageHash(TestDirectory packageSource, SimpleTestPack } } - [Fact] - public async Task InstallPackageAsync_LegacyPackageRefProjects_Duality() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task InstallPackageAsync_LegacyPackageRefProjects_Duality(bool usePackageSpecFactory) { using (var packageSource = TestDirectory.Create()) { @@ -1105,11 +1134,12 @@ public async Task InstallPackageAsync_LegacyPackageRefProjects_Duality() var projectServicesB = new TestProjectSystemServices(); - var legacyPRProjectB = new LegacyPackageReferenceProject( + var legacyPRProjectB = CreateLegacyPackageReferenceProject( vsProjectAdapterB, Guid.NewGuid().ToString(), projectServicesB, - _threadingService); + _threadingService, + usePackageSpecFactory); var projectPathA = Path.Combine(testSolutionManager.TestDirectory, "ProjectA"); var fullProjectPathA = Path.Combine(projectPathA, "project1.csproj"); @@ -1133,11 +1163,12 @@ public async Task InstallPackageAsync_LegacyPackageRefProjects_Duality() ProjectPath = fullProjectPathB }); - var legacyPRProjectA = new LegacyPackageReferenceProject( + var legacyPRProjectA = CreateLegacyPackageReferenceProject( vsProjectAdapterA, Guid.NewGuid().ToString(), projectServicesA, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProjectB); testSolutionManager.NuGetProjects.Add(legacyPRProjectA); @@ -1166,8 +1197,10 @@ public async Task InstallPackageAsync_LegacyPackageRefProjects_Duality() } } - [Fact] - public async Task InstallPackageAsync_LegacyPackageRefProjects_developmentDependency() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task InstallPackageAsync_LegacyPackageRefProjects_developmentDependency(bool usePackageSpecFactory) { using (var packageSource = TestDirectory.Create()) { @@ -1206,11 +1239,12 @@ public async Task InstallPackageAsync_LegacyPackageRefProjects_developmentDepend var projectServicesA = new TestProjectSystemServices(); - var legacyPRProjectA = new LegacyPackageReferenceProject( + var legacyPRProjectA = CreateLegacyPackageReferenceProject( vsProjectAdapterA, Guid.NewGuid().ToString(), projectServicesA, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProjectA); var testLogger = new TestLogger(); @@ -1261,8 +1295,10 @@ public async Task InstallPackageAsync_LegacyPackageRefProjects_developmentDepend } } - [Fact] - public async Task LegacyPackageRefProjects_UpdatePackage_KeepExistingMetadata() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task LegacyPackageRefProjects_UpdatePackage_KeepExistingMetadata(bool usePackageSpecFactory) { using (var packageSource = TestDirectory.Create()) { @@ -1312,11 +1348,12 @@ public async Task LegacyPackageRefProjects_UpdatePackage_KeepExistingMetadata() SuppressParent = LibraryIncludeFlags.None }); - var legacyPRProjectA = new LegacyPackageReferenceProject( + var legacyPRProjectA = CreateLegacyPackageReferenceProject( vsProjectAdapterA, Guid.NewGuid().ToString(), projectServicesA, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProjectA); var testLogger = new TestLogger(); @@ -1368,8 +1405,10 @@ await nuGetPackageManager.ExecuteNuGetProjectActionsAsync( } } - [Fact] - public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_MissingProjectsInSolution() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_MissingProjectsInSolution(bool usePackageSpecFactory) { using (var packageSource = TestDirectory.Create()) { @@ -1416,11 +1455,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Missing LibraryDependencyTarget.Package) }); - var legacyPRProjectB = new LegacyPackageReferenceProject( + var legacyPRProjectB = CreateLegacyPackageReferenceProject( vsProjectAdapterB, Guid.NewGuid().ToString(), projectServicesB, - _threadingService); + _threadingService, + usePackageSpecFactory); var projectPathA = Path.Combine(testSolutionManager.TestDirectory, "ProjectA"); var fullProjectPathA = Path.Combine(projectPathA, "project1.csproj"); @@ -1444,11 +1484,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Missing ProjectPath = fullProjectPathB }); - var legacyPRProjectA = new LegacyPackageReferenceProject( + var legacyPRProjectA = CreateLegacyPackageReferenceProject( vsProjectAdapterA, Guid.NewGuid().ToString(), projectServicesA, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProjectB); testSolutionManager.NuGetProjects.Add(legacyPRProjectA); @@ -1520,8 +1561,10 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Missing /// Unload projectB & projectC. Ensure the full restore graph is loaded /// /// - [Fact] - public async Task DependencyGraphRestoreUtility_WithMissingMultiLevelProjectClosure_Succeeds() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task DependencyGraphRestoreUtility_WithMissingMultiLevelProjectClosure_Succeeds(bool usePackageSpecFactory) { using (var packageSource = TestDirectory.Create()) { @@ -1567,11 +1610,12 @@ public async Task DependencyGraphRestoreUtility_WithMissingMultiLevelProjectClos LibraryDependencyTarget.Package) }); - var legacyPRProjectC = new LegacyPackageReferenceProject( + var legacyPRProjectC = CreateLegacyPackageReferenceProject( vsProjectAdapterC, Guid.NewGuid().ToString(), projectServicesC, - _threadingService); + _threadingService, + usePackageSpecFactory); var fullProjectPathB = Path.Combine(testSolutionManager.TestDirectory, "ProjectB", "project2.csproj"); var projectNamesB = new ProjectNames( @@ -1603,11 +1647,12 @@ public async Task DependencyGraphRestoreUtility_WithMissingMultiLevelProjectClos ProjectPath = fullProjectPathC }); - var legacyPRProjectB = new LegacyPackageReferenceProject( + var legacyPRProjectB = CreateLegacyPackageReferenceProject( vsProjectAdapterB, Guid.NewGuid().ToString(), projectServicesB, - _threadingService); + _threadingService, + usePackageSpecFactory); var projectPathA = Path.Combine(testSolutionManager.TestDirectory, "ProjectA"); var fullProjectPathA = Path.Combine(projectPathA, "project1.csproj"); @@ -1631,11 +1676,12 @@ public async Task DependencyGraphRestoreUtility_WithMissingMultiLevelProjectClos ProjectPath = fullProjectPathB }); - var legacyPRProjectA = new LegacyPackageReferenceProject( + var legacyPRProjectA = CreateLegacyPackageReferenceProject( vsProjectAdapterA, Guid.NewGuid().ToString(), projectServicesA, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProjectC); testSolutionManager.NuGetProjects.Add(legacyPRProjectB); testSolutionManager.NuGetProjects.Add(legacyPRProjectA); @@ -1706,8 +1752,10 @@ public async Task DependencyGraphRestoreUtility_WithMissingMultiLevelProjectClos } } - [Fact] - public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_PackagesLockFile_ResolveExactVersion() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_PackagesLockFile_ResolveExactVersion(bool usePackageSpecFactory) { using (var packageSource = TestDirectory.Create()) { @@ -1756,11 +1804,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Package LibraryDependencyTarget.Package) }); - var legacyPRProjectA = new LegacyPackageReferenceProject( + var legacyPRProjectA = CreateLegacyPackageReferenceProject( vsProjectAdapterA, Guid.NewGuid().ToString(), projectAServices, - _threadingService); + _threadingService, + usePackageSpecFactory); //projectB var projectBPath = Path.Combine(testSolutionManager.TestDirectory, "projectB"); @@ -1788,11 +1837,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Package LibraryDependencyTarget.Package) }); - var legacyPRProjectB = new LegacyPackageReferenceProject( + var legacyPRProjectB = CreateLegacyPackageReferenceProject( vsProjectAdapterB, Guid.NewGuid().ToString(), projectBServices, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProjectA); testSolutionManager.NuGetProjects.Add(legacyPRProjectB); @@ -1874,8 +1924,10 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Package } } - [Fact] - public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_PackagesLockFile_P2PReference() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_PackagesLockFile_P2PReference(bool usePackageSpecFactory) { using (var packageSource = TestDirectory.Create()) { @@ -1923,11 +1975,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Package SuppressParent = LibraryIncludeFlags.All }); - var legacyPRProjectB = new LegacyPackageReferenceProject( + var legacyPRProjectB = CreateLegacyPackageReferenceProject( vsProjectAdapterB, Guid.NewGuid().ToString(), projectServicesB, - _threadingService); + _threadingService, + usePackageSpecFactory); projectTargetFrameworkStr = "net461"; var projectPathA = Path.Combine(testSolutionManager.TestDirectory, "ProjectA"); @@ -1962,11 +2015,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Package ProjectPath = fullProjectPathB }); - var legacyPRProjectA = new LegacyPackageReferenceProject( + var legacyPRProjectA = CreateLegacyPackageReferenceProject( vsProjectAdapterA, Guid.NewGuid().ToString(), projectServicesA, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProjectB); testSolutionManager.NuGetProjects.Add(legacyPRProjectA); @@ -2042,8 +2096,10 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_Package } } - [Fact] - public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_BuildTransitive() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_BuildTransitive(bool usePackageSpecFactory) { using (var packageSource = TestDirectory.Create()) { @@ -2091,11 +2147,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_BuildTr LibraryDependencyTarget.Package), }); - var legacyPRProject2 = new LegacyPackageReferenceProject( + var legacyPRProject2 = CreateLegacyPackageReferenceProject( vsProjectAdapter2, Guid.NewGuid().ToString(), projectServicesB, - _threadingService); + _threadingService, + usePackageSpecFactory); var projectPath1 = Path.Combine(testSolutionManager.TestDirectory, "Project1"); var fullProjectPath1 = Path.Combine(projectPath1, "project1.csproj"); @@ -2118,11 +2175,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_BuildTr ProjectPath = fullProjectPath2 }); - var legacyPRProject1 = new LegacyPackageReferenceProject( + var legacyPRProject1 = CreateLegacyPackageReferenceProject( vsProjectAdapterA, Guid.NewGuid().ToString(), projectServices1, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProject1); testSolutionManager.NuGetProjects.Add(legacyPRProject2); @@ -2181,8 +2239,10 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_Restore_BuildTr } } - [Fact] - public async Task DependencyGraphRestoreUtility_LegacyPackageRef_CPVM_Restore() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task DependencyGraphRestoreUtility_LegacyPackageRef_CPVM_Restore(bool usePackageSpecFactory) { var packageA = (PackageId: "packageA", Version: "1.2.3"); var packageB = (PackageId: "packageB", Version: "3.4.5"); @@ -2237,11 +2297,12 @@ public async Task DependencyGraphRestoreUtility_LegacyPackageRef_CPVM_Restore() LibraryDependencyTarget.Package), }); - var legacyPRProject = new LegacyPackageReferenceProject( + var legacyPRProject = CreateLegacyPackageReferenceProject( vsProjectAdapter, Guid.NewGuid().ToString(), projectServices, - _threadingService); + _threadingService, + usePackageSpecFactory); testSolutionManager.NuGetProjects.Add(legacyPRProject); var packageContextB = new SimpleTestPackageContext(packageB.PackageId, packageB.Version); @@ -2349,5 +2410,27 @@ private ISettings PopulateSettingsWithSources(SourceRepositoryProvider sourceRep return settings; } + + private LegacyPackageReferenceProject CreateLegacyPackageReferenceProject( + IVsProjectAdapter projectAdapter, + string projectId, + ILegacyPackageReferenceProjectServices projectServices, + IVsProjectThreadingService threadingService, + bool usePackageSpecFactory) + { + var environmentVariables = new TestEnvironmentVariableReader(new Dictionary() + { + { PackageSpecFactory.EnvironmentVariableName, usePackageSpecFactory.ToString() } + }); + + var testProject = new LegacyPackageReferenceProject( + projectAdapter, + projectId, + projectServices, + threadingService, + environmentVariables); + + return testProject; + } } } 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 4b91b0f48e3..03e68cb3ec0 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 @@ -553,8 +553,11 @@ await PerformOperationAsync(async (projectManager) => } } - [Fact] - public async Task GetInstalledAndTransitivePackagesAsync_TransitiveOriginsWithLegacyPackageReferenceProject_OneTransitiveOriginAsync() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetInstalledAndTransitivePackagesAsync_TransitiveOriginsWithLegacyPackageReferenceProject_OneTransitiveOriginAsync( + bool usePackageSpecFactory) { // packageA_2.15.3 -> packageB_1.0.0 -> packageC_2.1.43 @@ -562,7 +565,7 @@ public async Task GetInstalledAndTransitivePackagesAsync_TransitiveOriginsWithLe using TestDirectory testDirectory = TestDirectory.Create(); // Arrange - LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, projectId, "[1.0.0, )", _threadingService); + LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, projectId, "[1.0.0, )", _threadingService, usePackageSpecFactory); NullSettings settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -696,9 +699,11 @@ await SimpleTestPackageUtility.CreateFullPackageAsync(pathContext.PackageSource, } [Theory] - [InlineData(true)] - [InlineData(false)] - public async Task GetInstalledAndTransitivePackagesAsync_TransitiveOriginsWithLegacyPackageReferenceProject_MultipleOriginsAsync(bool useSameVersions) + [InlineData(true, true)] + [InlineData(true, false)] + [InlineData(false, true)] + [InlineData(false, false)] + public async Task GetInstalledAndTransitivePackagesAsync_TransitiveOriginsWithLegacyPackageReferenceProject_MultipleOriginsAsync(bool useSameVersions, bool usePackageSpecFactory) { // case useSameversions = true // packageX_3.0.0 -> packageD_0.1.1 @@ -731,7 +736,7 @@ public async Task GetInstalledAndTransitivePackagesAsync_TransitiveOriginsWithLe }, }; - LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, projectId, _threadingService, onedep); + LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, projectId, _threadingService, onedep, usePackageSpecFactory); NullSettings settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(NullLogger.Instance, settings); @@ -1383,14 +1388,16 @@ public async Task GetPackageFoldersAsync_CpsProject_ReturnsPackageFolderAsync() Assert.Equal(1, folders.Count); // only globalPackagesFolder is listed } - [Fact] - public async Task GetPackageFoldersAsync_LegacyProject_ReturnsPackageFolderAsync() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageFoldersAsync_LegacyProject_ReturnsPackageFolderAsync(bool usePackageSpecFactory) { string projectId = Guid.NewGuid().ToString(); using TestDirectory testDirectory = TestDirectory.Create(); // Arrange - LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, projectId, "[1.0.0, )", _threadingService); + LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, projectId, "[1.0.0, )", _threadingService, usePackageSpecFactory); NullSettings settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(_logger, settings); @@ -1429,14 +1436,16 @@ public async Task GetPackageFoldersAsync_LegacyProject_ReturnsPackageFolderAsync Assert.Equal(1, folders.Count); } - [Fact] - public async Task GetPackageFoldersAsync_LegacyProjectWithFallbackFolder_ReturnsPackageFoldersAsync() + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task GetPackageFoldersAsync_LegacyProjectWithFallbackFolder_ReturnsPackageFoldersAsync(bool usePackageSpecFactory) { string projectId = Guid.NewGuid().ToString(); using TestDirectory testDirectory = TestDirectory.Create(); // Arrange - LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, projectId, "[1.0.0, )", _threadingService); + LegacyPackageReferenceProject testProject = CreateLegacyPackageReferenceProject(testDirectory, projectId, "[1.0.0, )", _threadingService, usePackageSpecFactory); NullSettings settings = NullSettings.Instance; var context = new DependencyGraphCacheContext(_logger, settings); diff --git a/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetRestoreTests.cs b/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetRestoreTests.cs index cff890ce240..270ed914f86 100644 --- a/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetRestoreTests.cs +++ b/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/DotnetRestoreTests.cs @@ -12,6 +12,7 @@ using FluentAssertions; using Microsoft.Internal.NuGet.Testing.SignedPackages; using Microsoft.Internal.NuGet.Testing.SignedPackages.ChildProcess; +using NuGet.Commands.Restore.Utility; using NuGet.Common; using NuGet.Frameworks; using NuGet.Packaging; @@ -804,7 +805,7 @@ public async Task DotnetRestore_OneLinePerRestore(bool useStaticGraphRestore, bo Dictionary environmentVariables = new Dictionary { - { "NUGET_USE_NEW_PACKAGESPEC_FACTORY", usePackageSpecFactory.ToString() } + { PackageSpecFactory.EnvironmentVariableName, usePackageSpecFactory.ToString() } }; // Act @@ -1412,7 +1413,7 @@ public async Task DotnetRestore_WithTargetFrameworksProperty_StaticGraphAndRegul var environmentVariables = new Dictionary { - { "NUGET_USE_NEW_PACKAGESPEC_FACTORY", usePackageSpecFactory.ToString() } + { PackageSpecFactory.EnvironmentVariableName, usePackageSpecFactory.ToString() } }; // Preconditions @@ -1447,7 +1448,7 @@ public void GenerateRestoreGraphFile_StandardAndStaticGraphRestore_AreEquivalent var environmentVariables = new Dictionary { - { "NUGET_USE_NEW_PACKAGESPEC_FACTORY", usePackageSpecFactory.ToString() } + { PackageSpecFactory.EnvironmentVariableName, usePackageSpecFactory.ToString() } }; _dotnetFixture.CreateDotnetNewProject(testDirectory, projectName1, " classlib", testOutputHelper: _testOutputHelper); @@ -1485,7 +1486,7 @@ public void GenerateRestoreGraphFile_StandardAndStaticGraphRestore_AuditProperti var environmentVariables = new Dictionary { - { "NUGET_USE_NEW_PACKAGESPEC_FACTORY", usePackageSpecFactory.ToString() } + { PackageSpecFactory.EnvironmentVariableName, usePackageSpecFactory.ToString() } }; string directoryBuildPropsPath = Path.Combine(testDirectory, "Directory.Build.props"); @@ -3046,7 +3047,7 @@ public async Task DotnetRestore_WithConditionalPrunedPackageReference_Succeeds(b var environmentVariables = new Dictionary() { - { "NUGET_USE_NEW_PACKAGESPEC_FACTORY", usePackageSpecFactory.ToString() } + { PackageSpecFactory.EnvironmentVariableName, usePackageSpecFactory.ToString() } }; var result = _dotnetFixture.RunDotnetExpectSuccess(workingDirectory, $"restore {projectFile}" + (isStaticGraphRestore ? " /p:RestoreUseStaticGraphEvaluation=true" : string.Empty), environmentVariables, testOutputHelper: _testOutputHelper); diff --git a/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs b/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs index 58cf9721beb..1ff9222496d 100644 --- a/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs +++ b/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs @@ -12,6 +12,7 @@ using FluentAssertions; using Microsoft.Internal.NuGet.Testing.SignedPackages.ChildProcess; using NuGet.Commands; +using NuGet.Commands.Restore.Utility; using NuGet.Common; using NuGet.Frameworks; using NuGet.Packaging; @@ -83,7 +84,7 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( var environmentVariables = new Dictionary(); environmentVariables.AddRange(_msbuildFixture.DefaultProcessEnvironmentVariables); - environmentVariables["NUGET_USE_NEW_PACKAGESPEC_FACTORY"] = usePackageSpecFactory.ToString(); + environmentVariables[PackageSpecFactory.EnvironmentVariableName] = usePackageSpecFactory.ToString(); // Act string args = $"/t:restore {pathContext.SolutionRoot} /p:RestorePackagesConfig=true /p:RestoreUseStaticGraphEvaluation={useStaticGraphRestore}"; @@ -598,7 +599,7 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( var environmentVariables = new Dictionary(); environmentVariables.AddRange(_msbuildFixture.DefaultProcessEnvironmentVariables); - environmentVariables["NUGET_USE_NEW_PACKAGESPEC_FACTORY"] = usePackageSpecFactory.ToString(); + environmentVariables[PackageSpecFactory.EnvironmentVariableName] = usePackageSpecFactory.ToString(); // Restore the project with a PackageReference which generates assets result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, $"/t:restore /p:RestoreUseStaticGraphEvaluation=true {project.ProjectPath}", ignoreExitCode: true, testOutputHelper: _testOutputHelper, environmentVariables); @@ -664,7 +665,7 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( var environmentVariables = new Dictionary(); environmentVariables.AddRange(_msbuildFixture.DefaultProcessEnvironmentVariables); - environmentVariables["NUGET_USE_NEW_PACKAGESPEC_FACTORY"] = usePackageSpecFactory.ToString(); + environmentVariables[PackageSpecFactory.EnvironmentVariableName] = usePackageSpecFactory.ToString(); // Restore the project with a PackageReference which generates assets var result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, $"/t:restore /p:RestoreUseStaticGraphEvaluation=true {project.ProjectPath}", ignoreExitCode: true, testOutputHelper: _testOutputHelper, environmentVariables); @@ -723,7 +724,7 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( var environmentVariables = new Dictionary(); environmentVariables.AddRange(_msbuildFixture.DefaultProcessEnvironmentVariables); - environmentVariables["NUGET_USE_NEW_PACKAGESPEC_FACTORY"] = usePackageSpecFactory.ToString(); + environmentVariables[PackageSpecFactory.EnvironmentVariableName] = usePackageSpecFactory.ToString(); // Restore the project with a PackageReference which generates assets var result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, $"/t:restore {project.ProjectPath} /p:RestoreSources=\"{relativePath}\"" + @@ -889,7 +890,7 @@ public async Task MsbuildRestore_WithMissingProjectReferences_HandlesProjectRefe var environmentVariables = new Dictionary(); environmentVariables.AddRange(_msbuildFixture.DefaultProcessEnvironmentVariables); - environmentVariables["NUGET_USE_NEW_PACKAGESPEC_FACTORY"] = usePackageSpecFactory.ToString(); + environmentVariables[PackageSpecFactory.EnvironmentVariableName] = usePackageSpecFactory.ToString(); var result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, $"/t:restore /p:RestoreUseStaticGraphEvaluation={restoreUseStaticGraphEvaluation} {projectA.ProjectPath}", ignoreExitCode: true, testOutputHelper: _testOutputHelper, environmentVariables); @@ -963,7 +964,7 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( var environmentVariables = new Dictionary(); environmentVariables.AddRange(_msbuildFixture.DefaultProcessEnvironmentVariables); - environmentVariables["NUGET_USE_NEW_PACKAGESPEC_FACTORY"] = usePackageSpecFactory.ToString(); + environmentVariables[PackageSpecFactory.EnvironmentVariableName] = usePackageSpecFactory.ToString(); // Act var result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, @@ -1943,7 +1944,7 @@ public async Task MsbuildRestore_WithPackageReferenceAndPackageVersion_RaisesNU1 var environmentVariables = new Dictionary(); environmentVariables.AddRange(_msbuildFixture.DefaultProcessEnvironmentVariables); - environmentVariables.Add("NUGET_USE_NEW_PACKAGESPEC_FACTORY", usePackageSpecFactory.ToString()); + environmentVariables.Add(PackageSpecFactory.EnvironmentVariableName, usePackageSpecFactory.ToString()); // Act CommandRunnerResult result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, $"/t:restore /p:RestoreUseStaticGraphEvaluation={useStaticGraphRestore} {projectA.ProjectPath}", ignoreExitCode: true, testOutputHelper: _testOutputHelper, environmentVariables); @@ -2204,7 +2205,7 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( var environmentVariables = new Dictionary(); environmentVariables.AddRange(_msbuildFixture.DefaultProcessEnvironmentVariables); - environmentVariables["NUGET_USE_NEW_PACKAGESPEC_FACTORY"] = usePackageSpecFactory.ToString(); + environmentVariables[PackageSpecFactory.EnvironmentVariableName] = usePackageSpecFactory.ToString(); var result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, $"/t:restore {project.ProjectPath}" + (useStaticGraphRestore ? " /p:RestoreUseStaticGraphEvaluation=\"true\"" : string.Empty), ignoreExitCode: true, testOutputHelper: _testOutputHelper, environmentVariables); result.Success.Should().BeTrue(because: result.AllOutput); diff --git a/test/TestUtilities/VisualStudio.Test.Utility/TestProjectSystemServices.cs b/test/TestUtilities/VisualStudio.Test.Utility/TestProjectSystemServices.cs index 5932b053ffd..06a3804a0bd 100644 --- a/test/TestUtilities/VisualStudio.Test.Utility/TestProjectSystemServices.cs +++ b/test/TestUtilities/VisualStudio.Test.Utility/TestProjectSystemServices.cs @@ -7,12 +7,14 @@ using Moq; using NuGet.Frameworks; using NuGet.LibraryModel; +using NuGet.PackageManagement.VisualStudio.Projects; using NuGet.ProjectManagement; using NuGet.ProjectModel; +using VSLangProj150; namespace Test.Utility { - public class TestProjectSystemServices : INuGetProjectServices + public class TestProjectSystemServices : ILegacyPackageReferenceProjectServices { public TestProjectSystemServices() { @@ -37,6 +39,8 @@ public TestProjectSystemServices() public IProjectScriptHostService ScriptService { get; } = Mock.Of(); + public VSProject4 Project4 { get; } = Mock.Of(); + public T GetGlobalService() where T : class { throw new NotImplementedException(); From 0218218530d12759396c86e16fdee37428e96bda Mon Sep 17 00:00:00 2001 From: Andy Zivkovic Date: Sun, 5 Apr 2026 05:07:32 +0930 Subject: [PATCH 2/5] work --- .../Projects/LegacyProjectAdapter.cs | 2 +- .../Projects/VsProjectAdapter.cs | 7 + .../Utility/EnvDTEProjectUtility.cs | 8 +- .../IVsProjectAdapter.cs | 8 + .../PublicAPI/net472/PublicAPI.Unshipped.txt | 3 + .../PublicAPI/net8.0/PublicAPI.Unshipped.txt | 3 + .../Utility/PackageSpecFactory.cs | 13 +- .../ProjectFactories.cs | 24 ++- .../LegacyPackageReferenceProjectTests.cs | 53 +---- .../ProjectSystems/TestVSProjectAdapter.cs | 22 ++ .../Utility/PackageSpecFactoryTests.cs | 61 ++++++ .../TestProjectSystemServices.cs | 193 ++++++++++++++++++ 12 files changed, 338 insertions(+), 59 deletions(-) create mode 100644 test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/Utility/PackageSpecFactoryTests.cs diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyProjectAdapter.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyProjectAdapter.cs index 6af37ef4061..c9bca13b962 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyProjectAdapter.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyProjectAdapter.cs @@ -143,7 +143,7 @@ private IReadOnlyList GetProjectReferences(string[] metadataNames) List references = new List(); foreach (Reference6 projectReference in _project4.References) { - if (projectReference.SourceProject != null && EnvDTEProjectUtility.IsSupported(projectReference.SourceProject, _projectAdapter.VsHierarchy)) + if (projectReference.SourceProject != null && _projectAdapter.IsSupported(projectReference)) { Array metadataElements; Array metadataValues; diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/VsProjectAdapter.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/VsProjectAdapter.cs index 8532351dd14..1cfef615ab0 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/VsProjectAdapter.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/VsProjectAdapter.cs @@ -17,6 +17,7 @@ using NuGet.Frameworks; using NuGet.ProjectManagement; using NuGet.VisualStudio; +using VSLangProj150; namespace NuGet.PackageManagement.VisualStudio { @@ -276,6 +277,12 @@ public bool IsCapabilityMatch(string capabilityExpression) return VsHierarchy.IsCapabilityMatch(capabilityExpression); } + public bool IsSupported(Reference6 projectReference) + { + ThreadHelper.ThrowIfNotOnUIThread(); + return EnvDTEProjectUtility.IsSupported(projectReference.SourceProject.Kind, VsHierarchy); + } + #endregion Getters } } diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/EnvDTEProjectUtility.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/EnvDTEProjectUtility.cs index 7ff1f74a139..470b786b406 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/EnvDTEProjectUtility.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Utility/EnvDTEProjectUtility.cs @@ -394,14 +394,14 @@ public static async Task IsSupportedAsync(EnvDTE.Project envDTEProject) Assumes.Present(envDTEProject); await NuGetUIThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); var hierarchy = await envDTEProject.ToVsHierarchyAsync(); - return IsSupported(envDTEProject, hierarchy); + return IsSupported(envDTEProject.Kind, hierarchy); } - public static bool IsSupported(EnvDTE.Project envDTEProject, IVsHierarchy hierarchy) + public static bool IsSupported(string projectKind, IVsHierarchy hierarchy) { ThreadHelper.ThrowIfNotOnUIThread(); - Assumes.Present(envDTEProject); + Assumes.Present(projectKind); Assumes.Present(hierarchy); if (VsHierarchyUtility.IsProjectCapabilityCompliant(hierarchy)) @@ -409,7 +409,7 @@ public static bool IsSupported(EnvDTE.Project envDTEProject, IVsHierarchy hierar return true; } - return envDTEProject.Kind != null && ProjectType.IsSupported(envDTEProject.Kind) && !VsHierarchyUtility.HasUnsupportedProjectCapability(hierarchy); + return projectKind != null && ProjectType.IsSupported(projectKind) && !VsHierarchyUtility.HasUnsupportedProjectCapability(hierarchy); } public async static Task GetNuGetProjectAsync(EnvDTE.Project project, ISolutionManager solutionManager) diff --git a/src/NuGet.Clients/NuGet.VisualStudio.Common/IVsProjectAdapter.cs b/src/NuGet.Clients/NuGet.VisualStudio.Common/IVsProjectAdapter.cs index da2797bd665..9357486e6dd 100644 --- a/src/NuGet.Clients/NuGet.VisualStudio.Common/IVsProjectAdapter.cs +++ b/src/NuGet.Clients/NuGet.VisualStudio.Common/IVsProjectAdapter.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Microsoft.VisualStudio.Shell.Interop; using NuGet.Frameworks; +using VSLangProj150; namespace NuGet.VisualStudio { @@ -77,5 +78,12 @@ public interface IVsProjectAdapter /// See /// bool IsCapabilityMatch(string capabilityExpression); + + /// + /// Checks whether a project reference supports NuGet. + /// + /// + /// + bool IsSupported(Reference6 projectReference); } } diff --git a/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt index 7dc5c58110b..ff66cde0473 100644 --- a/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt @@ -1 +1,4 @@ #nullable enable +const NuGet.Commands.Restore.Utility.PackageSpecFactory.EnvironmentVariableName = "NUGET_USE_NEW_PACKAGESPEC_FACTORY" -> string! +NuGet.Commands.SignArgs.AllowUntrustedRoot.get -> bool +NuGet.Commands.SignArgs.AllowUntrustedRoot.set -> void diff --git a/src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Unshipped.txt index 7dc5c58110b..ff66cde0473 100644 --- a/src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Unshipped.txt @@ -1 +1,4 @@ #nullable enable +const NuGet.Commands.Restore.Utility.PackageSpecFactory.EnvironmentVariableName = "NUGET_USE_NEW_PACKAGESPEC_FACTORY" -> string! +NuGet.Commands.SignArgs.AllowUntrustedRoot.get -> bool +NuGet.Commands.SignArgs.AllowUntrustedRoot.set -> void diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/PackageSpecFactory.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/PackageSpecFactory.cs index ecb59c1e3bc..33fb6f3abe1 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/PackageSpecFactory.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/PackageSpecFactory.cs @@ -471,7 +471,7 @@ private static string[] GetFallbackFolders(string? startupDirectory, string proj { // Fallback folders var currentFallbackFolders = GetValue( - () => fallbackFoldersOverride?.Select(e => UriUtility.GetAbsolutePath(startupDirectory, e)).ToArray(), + () => string.IsNullOrEmpty(startupDirectory) ? null : fallbackFoldersOverride?.Select(e => UriUtility.GetAbsolutePath(startupDirectory, e)).ToArray(), () => MSBuildRestoreUtility.ContainsClearKeyword(fallbackFolders) ? Array.Empty() : null, () => fallbackFolders?.Select(e => UriUtility.GetAbsolutePath(projectDirectory, e)).ToArray(), () => SettingsUtility.GetFallbackPackageFolders(settings).ToArray()); @@ -496,7 +496,10 @@ private static (bool IsEnabled, bool IsVersionOverrideDisabled, bool IsCentralPa { if (projectStyle == ProjectStyle.PackageReference) { - bool isEnabled = IsPropertyTrue(project, "_CentralPackageVersionsEnabled"); + bool isEnabled = + MSBuildStringUtility.IsTrue(project.GetProperty("BuildingInsideVisualStudio")) + ? IsPropertyTrue(project, "ManagePackageVersionsCentrally") + : IsPropertyTrue(project, "_CentralPackageVersionsEnabled"); bool isVersionOverrideDisabled = IsPropertyFalse(project, "CentralPackageVersionOverrideEnabled"); bool isCentralPackageTransitivePinningEnabled = IsPropertyTrue(project, "CentralPackageTransitivePinningEnabled"); bool isCentralPackageFloatingVersionsEnabled = IsPropertyTrue(project, "CentralPackageFloatingVersionsEnabled"); @@ -842,7 +845,7 @@ private static string[] GetSources(string? startupDirectory, string projectDirec { // Sources var currentSources = GetValue( - () => sourcesOverride?.Select(MSBuildRestoreUtility.FixSourcePath).Select(e => UriUtility.GetAbsolutePath(startupDirectory, e)).ToArray(), + () => string.IsNullOrEmpty(startupDirectory) ? null : sourcesOverride?.Select(MSBuildRestoreUtility.FixSourcePath).Select(e => UriUtility.GetAbsolutePath(startupDirectory, e)).ToArray(), () => MSBuildRestoreUtility.ContainsClearKeyword(sources) ? Array.Empty() : null, () => sources?.Select(MSBuildRestoreUtility.FixSourcePath).Select(e => UriUtility.GetAbsolutePath(projectDirectory, e)).ToArray(), () => (PackageSourceProvider.LoadPackageSources(settings)).Where(e => e.IsEnabled).Select(e => e.Source).ToArray()); @@ -930,7 +933,7 @@ internal static bool IsPropertyTrue(this ITargetFramework project, string proper return defaultValue; } - return string.Equals(value, bool.TrueString, StringComparison.OrdinalIgnoreCase); + return string.Equals(value!.Trim(), bool.TrueString, StringComparison.OrdinalIgnoreCase); } internal static bool IsPropertyFalse(this ITargetFramework project, string propertyName, bool defaultValue = false) @@ -942,7 +945,7 @@ internal static bool IsPropertyFalse(this ITargetFramework project, string prope return defaultValue; } - return string.Equals(value, bool.FalseString, StringComparison.OrdinalIgnoreCase); + return string.Equals(value!.Trim(), bool.FalseString, StringComparison.OrdinalIgnoreCase); } internal static bool IsMetadataTrue(this IItem item, string metadataName, bool defaultValue = false) diff --git a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectFactories.cs b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectFactories.cs index ec29b6f8529..6a68d4e4fe8 100644 --- a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectFactories.cs +++ b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectFactories.cs @@ -21,6 +21,7 @@ using NuGet.Versioning; using NuGet.VisualStudio; using Test.Utility; +using VSLangProj150; using Xunit; using Xunit.Abstractions; @@ -106,9 +107,13 @@ internal static IVsProjectAdapter CreateProjectAdapter(string fullPath, Mock x.FullProjectPath) - .Returns(Path.Combine(fullPath, "foo.csproj")); + .Returns(projectFilePath); + projectAdapter + .Setup(x => x.ProjectDirectory) + .Returns(fullPath); projectAdapter .Setup(x => x.GetTargetFramework()) .Returns(NuGetFramework.Parse("netstandard13")); @@ -119,6 +124,19 @@ internal static IVsProjectAdapter CreateProjectAdapter(string fullPath, Mock x.GetMSBuildProjectExtensionsPath()) .Returns(testMSBuildProjectExtensionsPath); + // MSBuild always defines these properties. Set them up so the PackageSpecFactory code path works. + projectBuildProperties + .Setup(x => x.GetPropertyValue("MSBuildProjectName")) + .Returns(Path.GetFileNameWithoutExtension(projectFilePath)); +#pragma warning disable CS0618 // GetPropertyValueWithDteFallback is obsolete + projectBuildProperties + .Setup(x => x.GetPropertyValueWithDteFallback(ProjectBuildProperties.MSBuildProjectExtensionsPath)) + .Returns(testMSBuildProjectExtensionsPath); + projectBuildProperties + .Setup(x => x.GetPropertyValueWithDteFallback(ProjectBuildProperties.TargetFrameworkMoniker)) + .Returns(NuGetFramework.Parse("netstandard13").DotNetFrameworkName); +#pragma warning restore CS0618 + return projectAdapter.Object; } @@ -138,6 +156,10 @@ internal static Mock CreateProjectAdapter(Mock x.BuildProperties) .Returns(projectBuildProperties.Object); + projectAdapter + .Setup(x => x.IsSupported(It.IsAny())) + .Returns(true); + return projectAdapter; } 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 b5f1d49d580..684924cf59e 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 @@ -245,16 +245,6 @@ public async Task GetPackageSpecsAsync_WithDefaultVersion_Succeeds(bool usePacka SpecValidationUtility.ValidateProjectSpec(actualRestoreSpec); Assert.Equal("1.0.0", actualRestoreSpec.Version.ToString()); - - // Verify - Mock.Get(projectAdapter) - .VerifyGet(x => x.Version, Times.AtLeastOnce); - Mock.Get(projectAdapter) - .VerifyGet(x => x.ProjectName, Times.AtLeastOnce); - Mock.Get(projectAdapter) - .VerifyGet(x => x.FullProjectPath, Times.AtLeastOnce); - Mock.Get(projectAdapter) - .Verify(x => x.GetTargetFramework(), Times.AtLeastOnce); } } @@ -272,6 +262,11 @@ public async Task GetPackageSpecsAsync_WithVersion_Succeeds(bool usePackageSpecF Mock.Get(projectAdapter) .SetupGet(x => x.Version) .Returns("2.2.3"); +#pragma warning disable CS0618 // GetPropertyValueWithDteFallback is obsolete + Mock.Get(projectAdapter.BuildProperties) + .Setup(x => x.GetPropertyValueWithDteFallback("Version")) + .Returns("2.2.3"); +#pragma warning restore CS0618 var projectServices = new TestProjectSystemServices(); @@ -294,10 +289,6 @@ public async Task GetPackageSpecsAsync_WithVersion_Succeeds(bool usePackageSpecF SpecValidationUtility.ValidateProjectSpec(actualRestoreSpec); Assert.Equal("2.2.3", actualRestoreSpec.Version.ToString()); - - // Verify - Mock.Get(projectAdapter) - .Verify(x => x.Version, Times.AtLeastOnce); } } @@ -367,9 +358,6 @@ public async Task GetPackageSpecsAsync_ReadSettingsWithRelativePaths(string? res var specFallback = actualRestoreSpec.RestoreMetadata.FallbackFolders; var expectedFolders = fallbackFolders != null ? MSBuildStringUtility.Split(fallbackFolders).Select(e => Path.Combine(testDirectory, e)) : SettingsUtility.GetFallbackPackageFolders(settings); Assert.True(Enumerable.SequenceEqual(expectedFolders.OrderBy(t => t), specFallback.OrderBy(t => t))); - - // Verify - projectBuildProperties.VerifyAll(); } } @@ -438,9 +426,6 @@ public async Task GetPackageSpecsAsync_ReadSettingsWithFullPaths(string? restore var specFallback = actualRestoreSpec.RestoreMetadata.FallbackFolders; var expectedFolders = fallbackFolders != null ? MSBuildStringUtility.Split(fallbackFolders) : SettingsUtility.GetFallbackPackageFolders(settings); Assert.True(Enumerable.SequenceEqual(expectedFolders.OrderBy(t => t), specFallback.OrderBy(t => t))); - - // Verify - projectBuildProperties.VerifyAll(); } } @@ -498,9 +483,6 @@ public async Task GetPackageSpecsAsync_WithPackageTargetFallback_Succeeds(bool u NuGetFramework.Parse("dnxcore50") }, ((FallbackFramework)actualTfi.FrameworkName).Fallback); - - // Verify - projectBuildProperties.VerifyAll(); } } @@ -551,12 +533,6 @@ public async Task GetPackageSpecsAsync_WithPackageReference_Succeeds(bool usePac Assert.NotNull(actualDependency); Assert.Equal("packageA", actualDependency.LibraryRange.Name); Assert.Equal(VersionRange.Parse("1.*"), actualDependency.LibraryRange.VersionRange); - - // Verify - Mock.Get(projectServices.ReferencesReader) - .Verify( - x => x.GetPackageReferencesAsync(framework, CancellationToken.None), - Times.AtLeastOnce); } } @@ -570,8 +546,6 @@ public async Task GetPackageSpecsAsync_WithProjectReference_Succeeds(bool usePac // Arrange using (var randomTestFolder = TestDirectory.Create()) { - var framework = NuGetFramework.Parse("netstandard13"); - var projectAdapter = CreateProjectAdapter(randomTestFolder); var projectServices = new TestProjectSystemServices(); @@ -603,12 +577,6 @@ public async Task GetPackageSpecsAsync_WithProjectReference_Succeeds(bool usePac var actualDependency = actualRestoreSpec.RestoreMetadata.TargetFrameworks.Single().ProjectReferences.Single(); Assert.NotNull(actualDependency); Assert.Equal("TestProjectA", actualDependency.ProjectUniqueName); - - // Verify - Mock.Get(projectServices.ReferencesReader) - .Verify( - x => x.GetProjectReferencesAsync(It.IsAny(), CancellationToken.None), - Times.AtLeastOnce); } } @@ -651,12 +619,6 @@ public async Task GetInstalledPackagesAsync_WhenValid_ReturnsPackageReferences(b Assert.Equal( "packageA.1.0.0", packageReference.PackageIdentity.ToString()); - - // Verify - Mock.Get(projectServices.ReferencesReader) - .Verify( - x => x.GetPackageReferencesAsync(framework, CancellationToken.None), - Times.AtLeastOnce); } } @@ -1630,9 +1592,6 @@ public async Task GetPackageSpecsAsync_WithRuntimeIdentifiers_GeneratesRuntimeGr // Assert runtime graph actualRestoreSpec.RuntimeGraph.Runtimes.Count.Should().Be(runtimeCount); actualRestoreSpec.RuntimeGraph.Supports.Count.Should().Be(supportsCount); - - // Verify - projectBuildProperties.VerifyAll(); } } @@ -1713,8 +1672,6 @@ public async Task GetPackageSpecs_WithWarningProperties(bool usePackageSpecFacto warningProperties.WarningsNotAsErrors.Should().HaveCount(1); warningProperties.WarningsAsErrors.Should().Contain(NuGetLogCode.NU1803); warningProperties.WarningsAsErrors.Should().HaveCount(1); - // Verify - projectBuildProperties.VerifyAll(); } [Theory] diff --git a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/TestVSProjectAdapter.cs b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/TestVSProjectAdapter.cs index 0c9bb559f01..ba25f8e3c2e 100644 --- a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/TestVSProjectAdapter.cs +++ b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/TestVSProjectAdapter.cs @@ -15,6 +15,7 @@ using NuGet.Frameworks; using NuGet.ProjectManagement; using NuGet.VisualStudio; +using VSLangProj150; namespace NuGet.PackageManagement.VisualStudio.Test { @@ -52,7 +53,23 @@ public TestVSProjectAdapter( _isCentralPackageVersionOverrideEnabled = isCentralPackageVersionOverrideEnabled; _isCentralPackageTransitivePinningEnabled = CentralPackageTransitivePinningEnabled; + Mock.Get(BuildProperties) + .Setup(x => x.GetPropertyValue("MSBuildProjectName")) + .Returns(projectNames.ShortName); + + Mock.Get(BuildProperties) + .Setup(x => x.GetPropertyValue("BuildingInsideVisualStudio")) + .Returns(bool.TrueString); + #pragma warning disable CS0618 // Type or member is obsolete + Mock.Get(BuildProperties) + .Setup(x => x.GetPropertyValueWithDteFallback(It.Is(x => x.Equals(ProjectBuildProperties.TargetFrameworkMoniker)))) + .Returns(NuGetFramework.Parse(targetFrameworkString).DotNetFrameworkName); + + Mock.Get(BuildProperties) + .Setup(x => x.GetPropertyValueWithDteFallback(It.Is(x => x.Equals(ProjectBuildProperties.MSBuildProjectExtensionsPath)))) + .Returns(GetMSBuildProjectExtensionsPath()); + Mock.Get(BuildProperties) .Setup(x => x.GetPropertyValueWithDteFallback(It.Is(x => x.Equals(ProjectBuildProperties.ManagePackageVersionsCentrally)))) .Returns(_isCPVMEnabled.ToString()); @@ -163,5 +180,10 @@ public bool IsCapabilityMatch(string capabilityExpression) { throw new NotImplementedException(); } + + public bool IsSupported(Reference6 projectReference) + { + return true; + } } } diff --git a/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/Utility/PackageSpecFactoryTests.cs b/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/Utility/PackageSpecFactoryTests.cs new file mode 100644 index 00000000000..a5a56889268 --- /dev/null +++ b/test/NuGet.Core.Tests/NuGet.Commands.Test/RestoreCommandTests/Utility/PackageSpecFactoryTests.cs @@ -0,0 +1,61 @@ +// 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 FluentAssertions; +using NuGet.ProjectManagement; +using Test.Utility; +using Xunit; + +namespace NuGet.Commands.Test.RestoreCommandTests.Utility; + +public class PackageSpecFactoryTests +{ + // The initial version of PackageSpecFactory is intended to maintain existing behavior with shared/common code, not + // fix discrepencies. Before PackageSpecFactory, CPS and Legacy projects in VS would read the ManagePackageVersionsCentrally + // property, while MSBuild and static restore reads the _CentalPackageVersionsEnabled property. The MSBuild targets sets + // _CentralPackageVersionsEnabled based on the value of ManagePackageVersionsCentrally but also if a Directory.Packages.props + // file exists/was imported. So, there are theoretical scenarios where the two properties could be different. + [Theory] + // BuildingInsideVisualStudio=true: reads ManagePackageVersionsCentrally + [InlineData("true", "true", null, true)] + [InlineData("true", "false", null, false)] + [InlineData("true", null, null, false)] + // BuildingInsideVisualStudio=false/absent: reads _CentralPackageVersionsEnabled + [InlineData("false", null, "true", true)] + [InlineData("false", null, "false", false)] + [InlineData("false", null, null, false)] + [InlineData(null, null, "true", true)] + [InlineData(null, null, "false", false)] + [InlineData(null, null, null, false)] + public void GetPackageSpec_CpmEnabledByCorrectPropertyForBuildContext( + string? buildingInsideVisualStudio, + string? managePackageVersionsCentrally, + string? centralPackageVersionsEnabled, + bool expectedCpmEnabled) + { + // Arrange + var factory = new TestPackageSpecFactory(outerBuild => + { + outerBuild.WithProperty("TargetFramework", "net8.0"); + if (buildingInsideVisualStudio is not null) + { + outerBuild.WithProperty("BuildingInsideVisualStudio", buildingInsideVisualStudio); + } + if (managePackageVersionsCentrally is not null) + { + outerBuild.WithProperty(ProjectBuildProperties.ManagePackageVersionsCentrally, managePackageVersionsCentrally); + } + if (centralPackageVersionsEnabled is not null) + { + outerBuild.WithProperty("_CentralPackageVersionsEnabled", centralPackageVersionsEnabled); + } + outerBuild.WithItem("PackageReference", "SomePackage", null); + }); + + // Act + var packageSpec = factory.Build(); + + // Assert + packageSpec.RestoreMetadata.CentralPackageVersionsEnabled.Should().Be(expectedCpmEnabled); + } +} diff --git a/test/TestUtilities/VisualStudio.Test.Utility/TestProjectSystemServices.cs b/test/TestUtilities/VisualStudio.Test.Utility/TestProjectSystemServices.cs index 06a3804a0bd..da4c68eccbd 100644 --- a/test/TestUtilities/VisualStudio.Test.Utility/TestProjectSystemServices.cs +++ b/test/TestUtilities/VisualStudio.Test.Utility/TestProjectSystemServices.cs @@ -3,13 +3,16 @@ using System; using System.Linq; +using System.Text; using System.Threading; +using EnvDTE; using Moq; using NuGet.Frameworks; using NuGet.LibraryModel; using NuGet.PackageManagement.VisualStudio.Projects; using NuGet.ProjectManagement; using NuGet.ProjectModel; +using VSLangProj; using VSLangProj150; namespace Test.Utility @@ -51,6 +54,10 @@ public void SetupInstalledPackages(NuGetFramework targetFramework, params Librar Mock.Get(ReferencesReader) .Setup(x => x.GetPackageReferencesAsync(targetFramework, CancellationToken.None)) .ReturnsAsync(dependencies.ToList()); + + Mock.Get(Project4) + .Setup(x => x.PackageReferences) + .Returns(new TestPackageReferences(dependencies)); } public void SetupProjectDependencies(params ProjectRestoreReference[] dependencies) @@ -59,6 +66,192 @@ public void SetupProjectDependencies(params ProjectRestoreReference[] dependenci .Setup(x => x.GetProjectReferencesAsync(It.IsAny(), CancellationToken.None)) .ReturnsAsync(dependencies.ToList()); + Mock.Get(Project4) + .SetupGet(x => x.References) + .Returns(() => + { + Mock references = new(MockBehavior.Strict); + + references.Setup(r => r.GetEnumerator()) + .Returns(() => + { + var projectReferences = dependencies.Select(d => + { + var mockReference = new Mock(MockBehavior.Strict); + + mockReference.SetupGet(x => x.SourceProject).Returns(() => + { + var mockProject = new Mock(MockBehavior.Strict); + mockProject.SetupGet(p => p.FullName).Returns(d.ProjectUniqueName); + return mockProject.Object; + }); + + mockReference.Setup(x => x.GetMetadata(It.IsAny(), out It.Ref.IsAny, out It.Ref.IsAny)) + .Callback(new GetMetadataDelegate((Array desiredMetadata, out Array metadataElements, out Array metadataValues) => + { + string?[] values = new string[desiredMetadata.Length]; + + for (var i = 0; i < values.Length; i++) + { + string metadataName = (string)desiredMetadata.GetValue(i); + string? value = GetMetadataValue(metadataName); + values[i] = value; + } + + metadataElements = desiredMetadata; + metadataValues = values; + + string? GetMetadataValue(string metadataName) + { + if (metadataName.Equals("ReferenceOutputAssembly", StringComparison.OrdinalIgnoreCase)) + { + return null; + } + else if (metadataName.Equals("FullPath", StringComparison.OrdinalIgnoreCase)) + { + return d.ProjectUniqueName; + } + else if (metadataName.Equals(ProjectItemProperties.IncludeAssets, StringComparison.OrdinalIgnoreCase)) + { + return NuGet.Common.MSBuildStringUtility.Convert(LibraryIncludeFlagUtils.GetFlagString(d.IncludeAssets)); + } + else if (metadataName.Equals(ProjectItemProperties.ExcludeAssets, StringComparison.OrdinalIgnoreCase)) + { + return NuGet.Common.MSBuildStringUtility.Convert(LibraryIncludeFlagUtils.GetFlagString(d.ExcludeAssets)); + } + else if (metadataName.Equals(ProjectItemProperties.PrivateAssets, StringComparison.OrdinalIgnoreCase)) + { + return NuGet.Common.MSBuildStringUtility.Convert(LibraryIncludeFlagUtils.GetFlagString(d.PrivateAssets)); + } + throw new NotImplementedException(metadataName); + } + })); + + return mockReference.Object; + }).ToList(); + return projectReferences.GetEnumerator(); + }); + + return references.Object; + }); + } + + private delegate void GetMetadataDelegate(Array parrbstrDesiredMetadata, out Array pparrbstrMetadataElements, out Array pparrbstrMetadataValues); + + private class TestPackageReferences : PackageReferences + { + private readonly LibraryDependency[] _dependencies; + public TestPackageReferences(LibraryDependency[] dependencies) + { + _dependencies = dependencies; + } + + public void AddOrUpdate(string bstrName, string bstrVersion, Array pbstrMetadataElements, Array pbstrMetadataValues) + { + throw new NotImplementedException(); + } + + public void Remove(string bstrName) + { + throw new NotImplementedException(); + } + + public bool TryGetReference(string bstrName, Array parrbstrDesiredMetadata, out string? pbstrVersion, out Array? pbstrMetadataElements, out Array? pbstrMetadataValues) + { + var package = _dependencies.FirstOrDefault(d => d.Name.Equals(bstrName, StringComparison.OrdinalIgnoreCase)); + if (package is null) + { + pbstrVersion = null; + pbstrMetadataElements = null; + pbstrMetadataValues = null; + return false; + } + + string?[] metadataValues = new string[parrbstrDesiredMetadata.Length]; + for (int i = 0; i < metadataValues.Length; i++) + { + string metadataName = (string)parrbstrDesiredMetadata.GetValue(i); + string? metadataValue = GetPackageReferenceMetadata(metadataName, package); + metadataValues[i] = metadataValue; + } + + pbstrVersion = package.LibraryRange.VersionRange?.OriginalString; + pbstrMetadataElements = parrbstrDesiredMetadata; + pbstrMetadataValues = metadataValues; + return true; + + string? GetPackageReferenceMetadata(string metadataName, LibraryDependency package) + { + if (metadataName.Equals(ProjectItemProperties.IsImplicitlyDefined, StringComparison.OrdinalIgnoreCase)) + { + return package.AutoReferenced.ToString(); + } + else if (metadataName.Equals("Version", StringComparison.OrdinalIgnoreCase)) + { + return package.LibraryRange.VersionRange?.OriginalString ?? package.LibraryRange.VersionRange?.ToString(); + } + else if (metadataName.Equals(ProjectItemProperties.VersionOverride, StringComparison.OrdinalIgnoreCase)) + { + return package.VersionOverride?.OriginalString; + } + else if (metadataName.Equals(ProjectItemProperties.GeneratePathProperty, StringComparison.OrdinalIgnoreCase)) + { + return package.GeneratePathProperty.ToString(); + } + else if (metadataName.Equals(ProjectItemProperties.Aliases, StringComparison.OrdinalIgnoreCase)) + { + return package.Aliases; + } + else if (metadataName.Equals(ProjectItemProperties.IncludeAssets, StringComparison.OrdinalIgnoreCase)) + { + string result = LibraryIncludeFlagUtils.GetFlagString(package.IncludeType); + return NuGet.Common.MSBuildStringUtility.Convert(result); + } + else if (metadataName.Equals(ProjectItemProperties.ExcludeAssets, StringComparison.OrdinalIgnoreCase)) + { + LibraryIncludeFlags excludeFlags = LibraryIncludeFlags.All & ~package.IncludeType; + string result = LibraryIncludeFlagUtils.GetFlagString(excludeFlags); + return NuGet.Common.MSBuildStringUtility.Convert(result); + } + else if (metadataName.Equals(ProjectItemProperties.PrivateAssets, StringComparison.OrdinalIgnoreCase)) + { + string result = LibraryIncludeFlagUtils.GetFlagString(package.SuppressParent); + return NuGet.Common.MSBuildStringUtility.Convert(result); + } + else if (metadataName.Equals(ProjectItemProperties.NoWarn, StringComparison.OrdinalIgnoreCase)) + { + var noWarn = package.NoWarn; + if (noWarn.Length == 0) { return null; } + StringBuilder sb = new StringBuilder(); + foreach (var code in noWarn) + { + if (sb.Length != 0) { sb.Append(';'); } + sb.Append(code); + } + return sb.ToString(); + } + throw new NotImplementedException(metadataName); + } + } + + public DTE DTE => throw new NotImplementedException(); + + public object Parent => throw new NotImplementedException(); + + public Project ContainingProject => throw new NotImplementedException(); + + public Array InstalledPackages + { + get + { + string[] packages = new string[_dependencies.Length]; + for (int i = 0; i < packages.Length; i++) + { + packages[i] = _dependencies[i].LibraryRange.Name; + } + return packages; + } + } } } } From a08af57e90abddd2c06597f11911ad6452caedb1 Mon Sep 17 00:00:00 2001 From: Andy Zivkovic Date: Sat, 11 Apr 2026 06:49:34 +0930 Subject: [PATCH 3/5] remove unnecessary nullable enable --- .../Projects/LegacyProjectAdapter.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyProjectAdapter.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyProjectAdapter.cs index c9bca13b962..0794362a076 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyProjectAdapter.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyProjectAdapter.cs @@ -1,8 +1,6 @@ // 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. -#nullable enable - using System; using System.Collections; using System.Collections.Generic; From 1448c127b65ef77fa931fb48ee5f5061bf35feac Mon Sep 17 00:00:00 2001 From: Andy Zivkovic Date: Sat, 18 Apr 2026 07:26:55 +0930 Subject: [PATCH 4/5] Fix build --- .../NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt | 2 -- .../NuGet.Commands/PublicAPI/net8.0/PublicAPI.Unshipped.txt | 2 -- 2 files changed, 4 deletions(-) diff --git a/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt index ff66cde0473..d2d8eae70ab 100644 --- a/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt @@ -1,4 +1,2 @@ #nullable enable const NuGet.Commands.Restore.Utility.PackageSpecFactory.EnvironmentVariableName = "NUGET_USE_NEW_PACKAGESPEC_FACTORY" -> string! -NuGet.Commands.SignArgs.AllowUntrustedRoot.get -> bool -NuGet.Commands.SignArgs.AllowUntrustedRoot.set -> void diff --git a/src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Unshipped.txt index ff66cde0473..d2d8eae70ab 100644 --- a/src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Unshipped.txt @@ -1,4 +1,2 @@ #nullable enable const NuGet.Commands.Restore.Utility.PackageSpecFactory.EnvironmentVariableName = "NUGET_USE_NEW_PACKAGESPEC_FACTORY" -> string! -NuGet.Commands.SignArgs.AllowUntrustedRoot.get -> bool -NuGet.Commands.SignArgs.AllowUntrustedRoot.set -> void From e2c2f644889e840522684b9b3dcf0229a42f34e0 Mon Sep 17 00:00:00 2001 From: Andy Zivkovic Date: Sat, 18 Apr 2026 07:48:16 +0930 Subject: [PATCH 5/5] Only read env var once in provider --- .../Projects/LegacyPackageReferenceProject.cs | 14 +++++--------- .../LegacyPackageReferenceProjectProvider.cs | 14 +++++++++++--- .../ProjectFactories.cs | 8 +------- .../LegacyPackageReferenceProjectTests.cs | 8 +------- .../LegacyPackageReferenceRestoreUtilityTests.cs | 8 +------- 5 files changed, 19 insertions(+), 33 deletions(-) diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProject.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProject.cs index 587e3eba883..8c9c7a88d83 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProject.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProject.cs @@ -54,7 +54,7 @@ public LegacyPackageReferenceProject( string projectId, ILegacyPackageReferenceProjectServices projectServices, IVsProjectThreadingService threadingService, - IEnvironmentVariableReader environmentVariableReader) + bool usePackageSpecFactory) : base(vsProjectAdapter.ProjectName, vsProjectAdapter.UniqueName, vsProjectAdapter.FullProjectPath) @@ -63,7 +63,6 @@ public LegacyPackageReferenceProject( Assumes.NotNullOrEmpty(projectId); Assumes.Present(projectServices); Assumes.Present(threadingService); - Assumes.Present(environmentVariableReader); _vsProjectAdapter = vsProjectAdapter; _threadingService = threadingService; @@ -78,11 +77,7 @@ public LegacyPackageReferenceProject( ProjectServices = projectServices; _projectServices = projectServices; - var packageSpecFactoryEnvVar = environmentVariableReader.GetEnvironmentVariable(PackageSpecFactory.EnvironmentVariableName); - _usePackageSpecFactory = - bool.FalseString.Equals(packageSpecFactoryEnvVar, StringComparison.OrdinalIgnoreCase) - ? false - : true; + _usePackageSpecFactory = usePackageSpecFactory; } public LegacyPackageReferenceProject( @@ -90,12 +85,13 @@ public LegacyPackageReferenceProject( string projectId, ILegacyPackageReferenceProjectServices projectServices, IVsProjectThreadingService threadingService, - NuGetFramework targetFramework) + NuGetFramework targetFramework, + bool usePackageSpecFactory) : this(vsProjectAdapter, projectId, projectServices, threadingService, - EnvironmentVariableWrapper.Instance) + usePackageSpecFactory) { Assumes.NotNull(targetFramework); TargetFramework = targetFramework; diff --git a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProjectProvider.cs b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProjectProvider.cs index 314df3f8216..20374951668 100644 --- a/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProjectProvider.cs +++ b/src/NuGet.Clients/NuGet.PackageManagement.VisualStudio/Projects/LegacyPackageReferenceProjectProvider.cs @@ -8,6 +8,8 @@ using Microsoft; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Utilities; +using NuGet.Commands.Restore.Utility; +using NuGet.Common; using NuGet.Frameworks; using NuGet.ProjectManagement; using NuGet.ProjectModel; @@ -27,6 +29,7 @@ public sealed class LegacyPackageReferenceProjectProvider : INuGetProjectProvide private readonly IVsProjectThreadingService _threadingService; private readonly Lazy _scriptExecutor; + private readonly bool _usePackageSpecFactory; public RuntimeTypeHandle ProjectType => typeof(LegacyPackageReferenceProject).TypeHandle; @@ -36,20 +39,24 @@ public LegacyPackageReferenceProjectProvider( Lazy scriptExecutor) : this(AsyncServiceProvider.GlobalProvider, threadingService, - scriptExecutor) + scriptExecutor, + EnvironmentVariableWrapper.Instance) { } public LegacyPackageReferenceProjectProvider( IAsyncServiceProvider vsServiceProvider, IVsProjectThreadingService threadingService, - Lazy scriptExecutor) + Lazy scriptExecutor, + IEnvironmentVariableReader environmentVariableReader) { Assumes.Present(vsServiceProvider); Assumes.Present(threadingService); Assumes.Present(scriptExecutor); + Assumes.Present(environmentVariableReader); _threadingService = threadingService; _scriptExecutor = scriptExecutor; + _usePackageSpecFactory = !bool.FalseString.Equals(PackageSpecFactory.EnvironmentVariableName, StringComparison.OrdinalIgnoreCase); } public NuGetProject TryCreateNuGetProject( @@ -75,7 +82,8 @@ public NuGetProject TryCreateNuGetProject( vsProjectAdapter.ProjectId, projectServices, _threadingService, - targetFramework); + targetFramework, + _usePackageSpecFactory); } /// diff --git a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectFactories.cs b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectFactories.cs index 6a68d4e4fe8..be3f48e0aeb 100644 --- a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectFactories.cs +++ b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectFactories.cs @@ -10,7 +10,6 @@ using System.Threading.Tasks; using Moq; using NuGet.Commands; -using NuGet.Commands.Restore.Utility; using NuGet.Commands.Test; using NuGet.Frameworks; using NuGet.LibraryModel; @@ -61,17 +60,12 @@ internal static LegacyPackageReferenceProject CreateLegacyPackageReferenceProjec framework, pkgDependencies); - var environmentVariables = new TestEnvironmentVariableReader(new Dictionary() - { - { PackageSpecFactory.EnvironmentVariableName, usePackageSpecFactory.ToString() } - }); - var testProject = new LegacyPackageReferenceProject( projectAdapter, projectId, projectServices, threadingService, - environmentVariables); + usePackageSpecFactory); return testProject; } 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 684924cf59e..98510b704ea 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 @@ -15,7 +15,6 @@ using Microsoft.VisualStudio.Threading; using Moq; using NuGet.Commands; -using NuGet.Commands.Restore.Utility; using NuGet.Commands.Test; using NuGet.Common; using NuGet.Configuration; @@ -2217,17 +2216,12 @@ private LegacyPackageReferenceProject CreateLegacyPackageReferenceProject( IVsProjectThreadingService threadingService, bool usePackageSpecFactory) { - var environmentVariables = new TestEnvironmentVariableReader(new Dictionary() - { - { PackageSpecFactory.EnvironmentVariableName, usePackageSpecFactory.ToString() } - }); - var testProject = new LegacyPackageReferenceProject( projectAdapter, projectId, projectServices, threadingService, - environmentVariables); + usePackageSpecFactory); return testProject; } diff --git a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceRestoreUtilityTests.cs b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceRestoreUtilityTests.cs index 87c3ad4208e..44723516d13 100644 --- a/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceRestoreUtilityTests.cs +++ b/test/NuGet.Clients.Tests/NuGet.PackageManagement.VisualStudio.Test/ProjectSystems/LegacyPackageReferenceRestoreUtilityTests.cs @@ -11,7 +11,6 @@ using System.Threading.Tasks; using Microsoft.VisualStudio.Sdk.TestFramework; using NuGet.Commands; -using NuGet.Commands.Restore.Utility; using NuGet.Common; using NuGet.Configuration; using NuGet.Frameworks; @@ -2418,17 +2417,12 @@ private LegacyPackageReferenceProject CreateLegacyPackageReferenceProject( IVsProjectThreadingService threadingService, bool usePackageSpecFactory) { - var environmentVariables = new TestEnvironmentVariableReader(new Dictionary() - { - { PackageSpecFactory.EnvironmentVariableName, usePackageSpecFactory.ToString() } - }); - var testProject = new LegacyPackageReferenceProject( projectAdapter, projectId, projectServices, threadingService, - environmentVariables); + usePackageSpecFactory); return testProject; }