From 35a75a25f91752f8dcd4d99e181b33626455ea92 Mon Sep 17 00:00:00 2001 From: Andy Zivkovic Date: Tue, 10 Jun 2025 17:00:19 +0930 Subject: [PATCH 1/3] Allow package update to use specific version --- .../Package/Update/IVersionChooser.cs | 19 + .../Commands/Package/Update/Package.cs | 63 ++++ .../Package/Update/PackageUpdateArgs.cs | 2 +- .../Package/Update/PackageUpdateCommand.cs | 5 +- .../Update/PackageUpdateCommandRunner.cs | 66 ++-- .../Commands/Package/Update/VersionChooser.cs | 28 +- .../Commands/Package/Update/PackageTests.cs | 116 ++++++ ...teCommandRunner.GetPackageToUpdateTests.cs | 183 ++++++++++ .../Test.Utility/Test.Utility.csproj | 7 +- .../Test.Utility/TestPackageSpecFactory.cs | 344 ++++++++++++++++++ 10 files changed, 793 insertions(+), 40 deletions(-) create mode 100644 src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/IVersionChooser.cs create mode 100644 src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/Package.cs create mode 100644 test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageTests.cs create mode 100644 test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunner.GetPackageToUpdateTests.cs create mode 100644 test/TestUtilities/Test.Utility/TestPackageSpecFactory.cs diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/IVersionChooser.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/IVersionChooser.cs new file mode 100644 index 00000000000..559fdcdb49f --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/IVersionChooser.cs @@ -0,0 +1,19 @@ +// 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.Threading; +using System.Threading.Tasks; +using NuGet.Versioning; + +namespace NuGet.CommandLine.XPlat.Commands.Package.Update +{ + internal interface IVersionChooser + { + Task GetLatestVersionAsync( + string packageId, + ILoggerWithColor logger, + CancellationToken cancellationToken); + } +} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/Package.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/Package.cs new file mode 100644 index 00000000000..5df44806a77 --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/Package.cs @@ -0,0 +1,63 @@ +// 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.Collections.Generic; +using System.CommandLine.Parsing; +using NuGet.Versioning; + +namespace NuGet.CommandLine.XPlat.Commands.Package.Update +{ + internal record Package + { + public required string Id { get; init; } + public required VersionRange? VersionRange { get; init; } + + internal static List Parse(ArgumentResult result) + { + if (result.Tokens.Count == 0) + { + return []; + } + + List packages = new List(result.Tokens.Count); + + foreach (var token in result.Tokens) + { + string? packageId; + VersionRange? newVersion; + int separatorIndex = token.Value.IndexOf('@'); + if (separatorIndex < 0) + { + packageId = token.Value; + newVersion = null; + } + else + { + packageId = token.Value.Substring(0, separatorIndex); + string versionString = token.Value.Substring(separatorIndex + 1); + if (string.IsNullOrEmpty(versionString)) + { + result.AddError("todo"); + return []; + } + if (!VersionRange.TryParse(versionString, out newVersion)) + { + result.AddError("todo"); + return []; + } + } + + var package = new Package + { + Id = packageId, + VersionRange = newVersion + }; + packages.Add(package); + } + + return packages; + } + } +} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateArgs.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateArgs.cs index f603d5e9f01..2f13bab9ef7 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateArgs.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateArgs.cs @@ -11,6 +11,6 @@ internal class PackageUpdateArgs { public required string Project { get; init; } - public IReadOnlyList? Packages { get; init; } + public required IReadOnlyList Packages { get; init; } } } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateCommand.cs index 91fbe62fae4..3eb54df4250 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateCommand.cs @@ -34,9 +34,10 @@ internal static void Register(Command packageCommand, Func get { var command = new DocumentedCommand("update", Strings.PackageUpdateCommand_Description, "https://aka.ms/dotnet/package/update"); - var packagesArguments = new Argument>("packages") + var packagesArguments = new Argument>("packages") { Arity = ArgumentArity.ZeroOrMore, + CustomParser = Package.Parse }; command.Arguments.Add(packagesArguments); @@ -49,7 +50,7 @@ internal static void Register(Command packageCommand, Func get { var logger = getLogger(); var project = args.GetValue(projectOption); - var packages = args.GetValue(packagesArguments); + var packages = args.GetValue(packagesArguments) ?? []; var commandArgs = new PackageUpdateArgs { diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateCommandRunner.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateCommandRunner.cs index a90901aba37..75d628d8447 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateCommandRunner.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateCommandRunner.cs @@ -61,7 +61,8 @@ internal static async Task Run(PackageUpdateArgs args, ILoggerWithColor log // Source provider will be needed to find the package version and to restore, so create it here. CachingSourceProvider sourceProvider = new CachingSourceProvider(new PackageSourceProvider(settings)); using SourceCacheContext sourceCacheContext = new(); - (PackageDependency? packageToUpdate, List? packageTfms) = await GetPackageToUpdateAsync(args.Packages, dgSpec.Projects.Single(), sourceProvider, settings, sourceCacheContext, logger, cancellationToken); + var versionChooser = new VersionChooser(sourceProvider, settings, sourceCacheContext); + (PackageDependency? packageToUpdate, List? packageTfms) = await GetPackageToUpdateAsync(args.Packages, dgSpec.Projects.Single(), versionChooser, settings, logger, cancellationToken); if (packageToUpdate is null) { @@ -153,12 +154,11 @@ private static async Task PreviewUpdatePackageReferenceAsync( return restoreResult.Single(); } - private static async Task<(PackageDependency?, List?)> GetPackageToUpdateAsync( - IReadOnlyList? packages, + internal static async Task<(PackageDependency?, List?)> GetPackageToUpdateAsync( + IReadOnlyList packages, PackageSpec project, - CachingSourceProvider sourceProvider, + IVersionChooser versionChooser, ISettings settings, - SourceCacheContext sourceCacheContext, ILoggerWithColor logger, CancellationToken cancellationToken) { @@ -181,7 +181,7 @@ private static async Task PreviewUpdatePackageReferenceAsync( return (null, null); } - var packageId = packages.Single(); + var package = packages.Single(); VersionRange? existingVersion = null; List? frameworks = null; @@ -189,7 +189,7 @@ private static async Task PreviewUpdatePackageReferenceAsync( { foreach (var dependency in tfm.Dependencies) { - if (string.Equals(packageId, dependency.Name, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(package.Id, dependency.Name, StringComparison.OrdinalIgnoreCase)) { if (frameworks is null) { @@ -201,11 +201,11 @@ private static async Task PreviewUpdatePackageReferenceAsync( if (project.RestoreMetadata.CentralPackageFloatingVersionsEnabled) { if (!tfm.CentralPackageVersions.TryGetValue( - packageId, + package.Id, out CentralPackageVersion? centralVersion)) { logger.LogMinimal( - Messages.Error_CouldNotFindPackageVersionForCpmPackage(packageId), + Messages.Error_CouldNotFindPackageVersionForCpmPackage(package.Id), ConsoleColor.Red); return (null, null); } @@ -225,7 +225,7 @@ private static async Task PreviewUpdatePackageReferenceAsync( if (tfmVersionRange != existingVersion) { logger.LogMinimal( - Messages.Unsupported_UpdatePackageWithDifferentPerTfmVersions(packageId, project.FilePath), + Messages.Unsupported_UpdatePackageWithDifferentPerTfmVersions(package.Id, project.FilePath), ConsoleColor.Red); return (null, null); } @@ -236,31 +236,43 @@ private static async Task PreviewUpdatePackageReferenceAsync( if (existingVersion is null) { - logger.LogMinimal(Messages.Error_PackageNotReferenced(packageId, project.FilePath), ConsoleColor.Red); + logger.LogMinimal(Messages.Error_PackageNotReferenced(package.Id, project.FilePath), ConsoleColor.Red); return (null, null); } - NuGetVersion? highestVersion = await VersionChooser.GetLatestVersionAsync( - packageId, - sourceProvider, - settings, - sourceCacheContext, - logger, - cancellationToken); - - if (highestVersion is null) + VersionRange newVersion; + if (package.VersionRange is null) { - logger.LogMinimal(Messages.Error_NoVersionsAvailable(packageId), ConsoleColor.Red); - return (null, null); - } + NuGetVersion? highestVersion = await versionChooser.GetLatestVersionAsync( + package.Id, + logger, + cancellationToken); + + if (highestVersion is null) + { + logger.LogMinimal(Messages.Error_NoVersionsAvailable(package.Id), ConsoleColor.Red); + return (null, null); + } + + if (existingVersion.MinVersion == highestVersion) + { + logger.LogMinimal(Messages.Warning_AlreadyHighestVersion(package.Id, highestVersion.OriginalVersion, project.FilePath), ConsoleColor.Red); + return (null, null); + } - if (existingVersion.MinVersion == highestVersion) + newVersion = new VersionRange(highestVersion); + } + else { - logger.LogMinimal(Messages.Warning_AlreadyHighestVersion(packageId, highestVersion.OriginalVersion, project.FilePath), ConsoleColor.Red); - return (null, null); + newVersion = package.VersionRange; + if (newVersion == existingVersion) + { + logger.LogMinimal("todo", ConsoleColor.Red); + return (null, null); + } } - PackageDependency packageToUpdate = new(packageId, new VersionRange(highestVersion)); + PackageDependency packageToUpdate = new(package.Id, newVersion); return (packageToUpdate, frameworks); } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/VersionChooser.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/VersionChooser.cs index 9ffa29f262c..82eff3de15d 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/VersionChooser.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/VersionChooser.cs @@ -9,26 +9,36 @@ using System.Threading.Tasks; using NuGet.Common; using NuGet.Configuration; -using NuGet.Protocol; using NuGet.Protocol.Core.Types; using NuGet.Versioning; namespace NuGet.CommandLine.XPlat.Commands.Package.Update { - internal static class VersionChooser + internal class VersionChooser : IVersionChooser { - internal static async Task GetLatestVersionAsync( - string packageId, - CachingSourceProvider sourceProvider, + private readonly ISourceRepositoryProvider _sourceProvider; + private readonly ISettings _settings; + private readonly SourceCacheContext _sourceCacheContext; + + public VersionChooser( + ISourceRepositoryProvider sourceProvider, ISettings settings, - SourceCacheContext sourceCacheContext, + SourceCacheContext sourceCacheContext) + { + _sourceProvider = sourceProvider; + _settings = settings; + _sourceCacheContext = sourceCacheContext; + } + + public async Task GetLatestVersionAsync( + string packageId, ILoggerWithColor logger, CancellationToken cancellationToken) { var sources = new List(); - foreach (PackageSource packageSource in SettingsUtility.GetEnabledSources(settings).NoAllocEnumerate()) + foreach (PackageSource packageSource in SettingsUtility.GetEnabledSources(_settings).NoAllocEnumerate()) { - SourceRepository sourceRepository = sourceProvider.CreateRepository(packageSource); + SourceRepository sourceRepository = _sourceProvider.CreateRepository(packageSource); sources.Add(sourceRepository); } @@ -37,7 +47,7 @@ internal static class VersionChooser { SourceRepository sourceRepository = sources[source]; // If package source is a local folder feed, it might not actually be async - lookups[source] = Task.Run(() => FindHighestPackageVersionAsync(sourceRepository, packageId, sourceCacheContext, logger, cancellationToken)); + lookups[source] = Task.Run(() => FindHighestPackageVersionAsync(sourceRepository, packageId, _sourceCacheContext, logger, cancellationToken)); } await Task.WhenAll(lookups); diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageTests.cs new file mode 100644 index 00000000000..b5c0765e27b --- /dev/null +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageTests.cs @@ -0,0 +1,116 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Collections.Generic; +using System.CommandLine; +using FluentAssertions; +using NuGet.Versioning; +using Xunit; + +using Pkg = NuGet.CommandLine.XPlat.Commands.Package.Update.Package; + +namespace NuGet.CommandLine.Xplat.Tests.Commands.Package.Update +{ + public class PackageTests + { + private RootCommand _command; + private Argument> _packagesArgument; + + public PackageTests() + { + _command = new RootCommand(); + + _packagesArgument = new Argument>("packages") + { + Arity = ArgumentArity.ZeroOrMore, + CustomParser = Pkg.Parse + }; + _command.Arguments.Add(_packagesArgument); + } + + [Fact] + public void Parse_OnePackage_ReturnsListOfOne() + { + // Arrange + var result = _command.Parse("packageId"); + + // Act + var packages = result.GetValue(_packagesArgument); + + // Assert + IReadOnlyList expects = [new Pkg() + { + Id = "packageId", + VersionRange = null + }]; + packages.Should().BeEquivalentTo(expects); + } + + [Fact] + public void Parse_TwoPackages_ReturnsListOfTwo() + { + // Arrange + var result = _command.Parse("packageId1 packageId2"); + // Act + var packages = result.GetValue(_packagesArgument); + // Assert + IReadOnlyList expects = [ + new Pkg() { Id = "packageId1", VersionRange = null }, + new Pkg() { Id = "packageId2", VersionRange = null } + ]; + packages.Should().BeEquivalentTo(expects); + } + + [Fact] + public void Parse_PackageWithVersion_ReturnsPackageWithVersion() + { + // Arrange + var result = _command.Parse("packageId@1.2.3"); + // Act + var packages = result.GetValue(_packagesArgument); + // Assert + IReadOnlyList expects = [new Pkg() + { + Id = "packageId", + VersionRange = VersionRange.Parse("1.2.3") + }]; + packages.Should().BeEquivalentTo(expects); + } + + [Fact] + public void Parse_PackageWithRangeSyntax_ReturnsPackageWithVersion() + { + // Arrange + var result = _command.Parse("packageId@[1.2.3,2.0.0)"); + // Act + var packages = result.GetValue(_packagesArgument); + // Assert + IReadOnlyList expects = [new Pkg() + { + Id = "packageId", + VersionRange = VersionRange.Parse("[1.2.3,2.0.0)") + }]; + packages.Should().BeEquivalentTo(expects); + } + + [Fact] + public void Parse_VersionWithNoId_ReturnsError() + { + // Arrange & Act + var result = _command.Parse("@1.2.3"); + + // Assert + result.Errors.Should().ContainSingle(); + } + + [Fact] + public void Parse_PackageWithInvalidVersion_ReturnsError() + { + // Arrange & Act + var result = _command.Parse("packageId@one"); + + // Assert + result.Errors.Should().ContainSingle(); + } + } +} diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunner.GetPackageToUpdateTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunner.GetPackageToUpdateTests.cs new file mode 100644 index 00000000000..5bc9c83ab41 --- /dev/null +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunner.GetPackageToUpdateTests.cs @@ -0,0 +1,183 @@ +// 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.Threading; +using System.Threading.Tasks; +using FluentAssertions; +using Moq; +using NuGet.CommandLine.XPlat; +using NuGet.CommandLine.XPlat.Commands.Package.Update; +using NuGet.Configuration; +using NuGet.ProjectModel; +using NuGet.Protocol.Core.Types; +using NuGet.Versioning; +using Test.Utility; +using Xunit; +using Xunit.Abstractions; + +using Pkg = NuGet.CommandLine.XPlat.Commands.Package.Update.Package; + +namespace NuGet.CommandLine.Xplat.Tests.Commands.Package.Update +{ + public class PackageUpdateCommandRunner_GetPackageToUpdateTests + { + private readonly ITestOutputHelper _output; + + public PackageUpdateCommandRunner_GetPackageToUpdateTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public async Task RequestSinglePackage_GetsRequestedVersion() + { + // Arrange + Pkg package = new() + { + Id = "Contoso.Utils", + VersionRange = new VersionRange(new NuGetVersion("1.2.3")) + }; + + PackageSpec packageSpec = new TestPackageSpecFactory("c:\\src\\my.csproj", builder => + { + builder.WithProperty("TargetFramework", "net9.0") + .WithItem("PackageReference", "Contoso.Utils", [new("Version", "1.0.0")]); + }) + .Build(); + + var versionChooser = new Mock(MockBehavior.Strict); + var logger = new Mock(); + + // Act + var (packageToUpdate, _) = await PackageUpdateCommandRunner.GetPackageToUpdateAsync( + [package], + packageSpec, + versionChooser.Object, + NullSettings.Instance, + logger.Object, + CancellationToken.None); + + // Assert + packageToUpdate.Should().NotBeNull(); + packageToUpdate.Id.Should().Be("Contoso.Utils"); + packageToUpdate.VersionRange.ToString().Should().Be("[1.2.3, )"); + logger.Invocations.Count.Should().Be(0); + } + + [Fact] + public async Task RequestPackageWithoutVersion_GetsLatestVersion() + { + // Arrange + Pkg package = new() + { + Id = "Contoso.Utils", + VersionRange = null + }; + + PackageSpec packageSpec = new TestPackageSpecFactory("c:\\src\\my.csproj", builder => + { + builder.WithProperty("TargetFramework", "net9.0") + .WithItem("PackageReference", "Contoso.Utils", [new("Version", "1.0.0")]); + }) + .Build(); + + var versionChooser = new Mock(MockBehavior.Strict); + versionChooser + .Setup(v => v.GetLatestVersionAsync("Contoso.Utils", It.IsAny(), It.IsAny())) + .ReturnsAsync(new NuGetVersion("3.4.5")); + + var sourceCacheContext = new SourceCacheContext(); + var logger = new Mock(); + + // Act + var (packageToUpdate, _) = await PackageUpdateCommandRunner.GetPackageToUpdateAsync( + [package], + packageSpec, + versionChooser.Object, + NullSettings.Instance, + logger.Object, + CancellationToken.None); + + // Assert + packageToUpdate.Should().NotBeNull(); + packageToUpdate.Id.Should().Be("Contoso.Utils"); + packageToUpdate.VersionRange.ToString().Should().Be("[3.4.5, )"); + logger.Invocations.Count.Should().Be(0); + } + + [Fact] + public async Task RequestSinglePackageWithRangeSyntax_GetsRequestedVersion() + { + // Arrange + Pkg package = new() + { + Id = "Contoso.Utils", + VersionRange = VersionRange.Parse("[1.2.3,2.0.0)") + }; + + PackageSpec packageSpec = new TestPackageSpecFactory("c:\\src\\my.csproj", builder => + { + builder.WithProperty("TargetFramework", "net9.0") + .WithItem("PackageReference", "Contoso.Utils", [new("Version", "1.0.0")]); + }) + .Build(); + + var versionChooser = new Mock(MockBehavior.Strict); + var logger = new Mock(); + + // Act + var (packageToUpdate, _) = await PackageUpdateCommandRunner.GetPackageToUpdateAsync( + [package], + packageSpec, + versionChooser.Object, + NullSettings.Instance, + logger.Object, + CancellationToken.None); + + // Assert + packageToUpdate.Should().NotBeNull(); + packageToUpdate.Id.Should().Be("Contoso.Utils"); + packageToUpdate.VersionRange.ToString().Should().Be("[1.2.3, 2.0.0)"); + logger.Invocations.Count.Should().Be(0); + } + + [Fact] + public async Task RequestPackageNotOnSource_LogsError() + { + // Arrange + Pkg package = new() + { + Id = "Contoso.Utils", + VersionRange = null + }; + + PackageSpec packageSpec = new TestPackageSpecFactory("c:\\src\\my.csproj", builder => + { + builder.WithProperty("TargetFramework", "net9.0") + .WithItem("PackageReference", "Contoso.Utils", [new("Version", "1.0.0")]); + }) + .Build(); + + var versionChooser = new Mock(MockBehavior.Strict); + versionChooser + .Setup(v => v.GetLatestVersionAsync("Contoso.Utils", It.IsAny(), It.IsAny())) + .ReturnsAsync((NuGetVersion?)null); + var logger = new Mock(); + + // Act + var (packageToUpdate, _) = await PackageUpdateCommandRunner.GetPackageToUpdateAsync( + [package], + packageSpec, + versionChooser.Object, + NullSettings.Instance, + logger.Object, + CancellationToken.None); + + // Assert + packageToUpdate.Should().BeNull(); + logger.Invocations.Count.Should().BeGreaterThan(0); + } + } +} diff --git a/test/TestUtilities/Test.Utility/Test.Utility.csproj b/test/TestUtilities/Test.Utility/Test.Utility.csproj index 99b72bd5ae1..21bd615ff39 100644 --- a/test/TestUtilities/Test.Utility/Test.Utility.csproj +++ b/test/TestUtilities/Test.Utility/Test.Utility.csproj @@ -1,4 +1,4 @@ - + $(TargetFrameworksUnitTest) $(NoWarn);CS1591 @@ -12,6 +12,11 @@ false + + + + + diff --git a/test/TestUtilities/Test.Utility/TestPackageSpecFactory.cs b/test/TestUtilities/Test.Utility/TestPackageSpecFactory.cs new file mode 100644 index 00000000000..66cdbe0cac1 --- /dev/null +++ b/test/TestUtilities/Test.Utility/TestPackageSpecFactory.cs @@ -0,0 +1,344 @@ +// 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.Generic; +using System.Linq; +using NuGet.Commands.Restore; +using NuGet.Commands.Restore.Utility; +using NuGet.Configuration; +using NuGet.Frameworks; +using NuGet.ProjectModel; + +namespace Test.Utility; + +public class TestPackageSpecFactory +{ + private string _fullPath; + private string _directory; + private TestTargetFramework _outerBuild; + private Dictionary? _targetFrameworks; + + public TestPackageSpecFactory(string fullPath, Action builder) + : this(fullPath, System.IO.Path.GetDirectoryName(fullPath) ?? throw new ArgumentException(message: "Could not get directory from path", paramName: nameof(fullPath)), builder) + { + } + + public TestPackageSpecFactory(string fullPath, string directory, Action builder) + { + _fullPath = string.IsNullOrWhiteSpace(fullPath) ? throw new ArgumentNullException(nameof(fullPath)) : fullPath; + _directory = string.IsNullOrWhiteSpace(directory) ? throw new ArgumentNullException(nameof(directory)) : directory; + + var tfBuilder = new TargetFrameworkBuilder(); + builder(tfBuilder); + + TestTargetFramework outerBuild = tfBuilder.ToTargetFramework(); + if (!outerBuild.Properties.TryGetValue("MSBuildProjectName", out string? msbuildProjectName)) + { + msbuildProjectName = System.IO.Path.GetFileNameWithoutExtension(_fullPath); + outerBuild.Properties["MSBuildProjectName"] = msbuildProjectName; + } + + string? targetFramework = outerBuild.GetProperty("TargetFramework"); + string? targetFrameworks = outerBuild.GetProperty("TargetFrameworks"); + + if (string.IsNullOrWhiteSpace(targetFramework) && string.IsNullOrWhiteSpace(targetFrameworks)) + { + throw new ArgumentException("TargetFramework or TargetFrameworks must be set in the outer build."); + } + else if (!string.IsNullOrWhiteSpace(targetFrameworks) && !string.IsNullOrWhiteSpace(targetFramework)) + { + throw new ArgumentException("Only one of TargetFramework or TargetFrameworks can be set in the outer build."); + } + + _outerBuild = outerBuild; + } + + public TestPackageSpecFactory WithInnerBuild(Action builder) + { + if (builder is null) + { + throw new ArgumentNullException(nameof(builder)); + } + + var tfBuilder = new TargetFrameworkBuilder(); + builder(tfBuilder); + + ITargetFramework innerBuild = tfBuilder.ToTargetFramework(_outerBuild); + + string? targetFramework = innerBuild.GetProperty("TargetFramework"); + if (string.IsNullOrWhiteSpace(targetFramework)) + { + throw new ArgumentException("TargetFramework must be set in the inner build."); + } + + _targetFrameworks ??= new Dictionary(StringComparer.OrdinalIgnoreCase); + +#if NETFRAMEWORK + if (_targetFrameworks.ContainsKey(targetFramework!)) + { + throw new InvalidOperationException($"Inner build for target framework '{targetFramework}' is already set."); + } + _targetFrameworks[targetFramework!] = innerBuild; +#else + if (!_targetFrameworks.TryAdd(targetFramework, innerBuild)) + { + throw new InvalidOperationException($"Inner build for target framework '{targetFramework}' is already set."); + } +#endif + + _targetFrameworks[targetFramework!] = innerBuild; + + return this; + } + + public PackageSpec Build(ISettings? settings = null) + { + Dictionary targetFrameworks; + if (_targetFrameworks is not null && _targetFrameworks.Count > 0) + { + targetFrameworks = _targetFrameworks; + var targetFrameworksProperty = _outerBuild.GetProperty("TargetFrameworks"); + if (string.IsNullOrWhiteSpace(targetFrameworksProperty)) + { + throw new InvalidOperationException("TargetFrameworks must be set in the outer build when multiple target frameworks are defined."); + } + + HashSet aliases = targetFrameworksProperty! + .Split([';'], StringSplitOptions.RemoveEmptyEntries) + .Select(alias => alias.Trim()) + .ToHashSet(StringComparer.OrdinalIgnoreCase); + + HashSet missingTargetFrameworks = new HashSet( + aliases.Where(alias => !targetFrameworks.ContainsKey(alias)), + StringComparer.OrdinalIgnoreCase); + HashSet extraTargetFrameworks = new HashSet( + targetFrameworks.Keys.Where(key => !aliases.Contains(key)), + StringComparer.OrdinalIgnoreCase); + + if (missingTargetFrameworks.Count > 0 || extraTargetFrameworks.Count > 0) + { + string errorMessage = "The TargetFrameworks property does not match the inner builds defined in the PackageSpec."; + if (missingTargetFrameworks.Count > 0) + { + errorMessage += $" Missing target frameworks: {string.Join(", ", missingTargetFrameworks)}."; + } + if (extraTargetFrameworks.Count > 0) + { + errorMessage += $" Extra target frameworks: {string.Join(", ", extraTargetFrameworks)}."; + } + throw new InvalidOperationException(errorMessage); + } + } + else + { + targetFrameworks = new Dictionary(StringComparer.OrdinalIgnoreCase); + string targetFramework = _outerBuild.GetProperty("TargetFramework") ?? throw new InvalidOperationException("TargetFramework must be set in the outer build."); + targetFrameworks[targetFramework] = _outerBuild; + } + + var project = new TestProject + { + FullPath = _fullPath, + Directory = _directory, + OuterBuild = _outerBuild, + TargetFrameworks = targetFrameworks + }; + + var packageSpec = PackageSpecFactory.GetPackageSpec(project, settings ?? NullSettings.Instance); + if (packageSpec is null) + { + throw new InvalidOperationException("Failed to create PackageSpec from the provided project."); + } + return packageSpec; + } + + public record TargetFrameworkBuilder + { + private readonly Dictionary _properties = new(StringComparer.OrdinalIgnoreCase); + private Dictionary>? _items; + + public TargetFrameworkBuilder WithProperty(string name, string value) + { + if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentNullException(nameof(name)); } + if (string.IsNullOrWhiteSpace(value)) { throw new ArgumentNullException(nameof(value)); } + +#if NETFRAMEWORK + if (_properties.ContainsKey(name)) + { + throw new InvalidOperationException($"Property '{name}' is already defined."); + } + _properties[name] = value; +#else + if (!_properties.TryAdd(name, value)) + { + throw new InvalidOperationException($"Property '{name}' is already defined."); + } +#endif + + return this; + } + + public TargetFrameworkBuilder WithItem(string itemType, string identity, KeyValuePair[]? metadata) + { + if (string.IsNullOrWhiteSpace(itemType)) { throw new ArgumentNullException(nameof(itemType)); } + if (string.IsNullOrWhiteSpace(identity)) { throw new ArgumentNullException(nameof(identity)); } + + TestItem item = new TestItem + { + Identity = identity, + Metadata = metadata?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value, StringComparer.OrdinalIgnoreCase) ?? [] + }; + + if (_items is null) + { + _items = new Dictionary>(StringComparer.OrdinalIgnoreCase); + } + + if (!_items.TryGetValue(itemType, out var itemList)) + { + itemList = new List(); + _items[itemType] = itemList; + } + itemList.Add(item); + + return this; + } + + internal TestTargetFramework ToTargetFramework(TestTargetFramework outerBuild) + { + foreach (var property in outerBuild.Properties) + { +#if NETFRAMEWORK + if (!_properties.ContainsKey(property.Key)) + { + _properties[property.Key] = property.Value; + } +#else + _properties.TryAdd(property.Key, property.Value); +#endif + } + + if (_items is not null) + { + foreach (var item in outerBuild.Items) + { + if (!_items!.ContainsKey(item.Key)) + { + _items[item.Key] = new List(); + } + _items[item.Key].AddRange(item.Value); + } + } + else + { + _items = outerBuild.Items; + } + + return ToTargetFramework(); + } + + internal TestTargetFramework ToTargetFramework() + { + SimulateDotnetSdk(_properties, _items); + + // Create a new TestTargetFramework with the accumulated properties and items + return new TestTargetFramework + { + Properties = new Dictionary(_properties, StringComparer.OrdinalIgnoreCase), + Items = _items ?? new Dictionary>(StringComparer.OrdinalIgnoreCase) + }; + + void SimulateDotnetSdk(Dictionary properties, Dictionary>? items) + { + if (!properties.TryGetValue("TargetFramework", out var targetFramework) || string.IsNullOrWhiteSpace(targetFramework)) + { + return; + } + + NuGetFramework? framework = null; + try + { + framework = NuGetFramework.Parse(targetFramework); + } + catch + { + } + + if (!properties.TryGetValue("TargetFrameworkIdentifier", out var targetFrameworkIdentifer) && framework is not null) + { + targetFrameworkIdentifer = framework.Framework; + properties["TargetFrameworkIdentifier"] = targetFrameworkIdentifer; + } + + if (!properties.TryGetValue("TargetFrameworkVersion", out var targetFrameworkVersion) && framework is not null) + { + targetFrameworkVersion = $"v{framework.Version.ToString()}"; + properties["TargetFrameworkVersion"] = targetFrameworkVersion; + } + + if (!properties.TryGetValue("TargetFrameworkMoniker", out var targetFrameworkMoniker)) + { + targetFrameworkMoniker = $"{targetFrameworkIdentifer},Version={targetFrameworkVersion}"; + properties["TargetFrameworkMoniker"] = targetFrameworkMoniker; + } + + if (!properties.TryGetValue("TargetPlatformIdentifier", out var targetPlatformIdentifier) && framework is not null) + { + targetPlatformIdentifier = framework.Platform; + } + + if (!string.IsNullOrWhiteSpace(targetPlatformIdentifier)) + { + if (!properties.TryGetValue("TargetPlatformVersion", out var targetPlatformVersion)) + { + targetPlatformVersion = framework?.PlatformVersion?.ToString(); + targetPlatformVersion = targetPlatformVersion is null ? "v1.0" : $"v{targetPlatformVersion}"; + properties["TargetPlatformVersion"] = targetPlatformVersion; + } + + if (!properties.TryGetValue("TargetPlatformMoniker", out var targetPlatformMoniker)) + { + targetPlatformMoniker = $"{targetPlatformIdentifier},Version={targetPlatformVersion}"; + properties["TargetPlatformMoniker"] = targetPlatformMoniker; + } + } + } + } + } + + private record TestProject : IProject + { + public required string FullPath { get; init; } + public required string Directory { get; init; } + public required ITargetFramework OuterBuild { get; init; } + public required IReadOnlyDictionary TargetFrameworks { get; init; } + } + + internal record TestTargetFramework : ITargetFramework + { + public required Dictionary Properties { get; init; } + public required Dictionary> Items { get; init; } + + public string GetProperty(string propertyName) => Properties.TryGetValue(propertyName, out var value) ? value : null!; + public IReadOnlyList GetItems(string itemType) => Items.TryGetValue(itemType, out var items) ? items : []; + } + + internal record TestItem : 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; + } + return null!; + } + } +} From 31cdb49e73b9eb28398b4163f599d515af4c39f6 Mon Sep 17 00:00:00 2001 From: Andy Zivkovic Date: Wed, 11 Jun 2025 11:14:24 +0930 Subject: [PATCH 2/3] feedback --- .../Commands/Package/Update/Package.cs | 6 ++-- .../Package/Update/PackageUpdateCommand.cs | 3 +- .../Update/PackageUpdateCommandRunner.cs | 2 +- .../NuGet.CommandLine.XPlat/Messages.cs | 15 ++++++++ .../Strings.Designer.cs | 36 +++++++++++++++++++ .../NuGet.CommandLine.XPlat/Strings.resx | 16 +++++++++ .../Commands/Package/Update/PackageTests.cs | 10 ++++-- .../Test.Utility/TestPackageSpecFactory.cs | 12 +++++-- 8 files changed, 91 insertions(+), 9 deletions(-) diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/Package.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/Package.cs index 5df44806a77..334db10f9d4 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/Package.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/Package.cs @@ -14,7 +14,7 @@ internal record Package public required string Id { get; init; } public required VersionRange? VersionRange { get; init; } - internal static List Parse(ArgumentResult result) + internal static IReadOnlyList Parse(ArgumentResult result) { if (result.Tokens.Count == 0) { @@ -39,12 +39,12 @@ internal static List Parse(ArgumentResult result) string versionString = token.Value.Substring(separatorIndex + 1); if (string.IsNullOrEmpty(versionString)) { - result.AddError("todo"); + result.AddError(Messages.Error_MissingVersion(token.Value)); return []; } if (!VersionRange.TryParse(versionString, out newVersion)) { - result.AddError("todo"); + result.AddError(Messages.Error_InvalidVersionRange(versionString)); return []; } } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateCommand.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateCommand.cs index 3eb54df4250..0d1eb8a75b8 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateCommand.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateCommand.cs @@ -34,8 +34,9 @@ internal static void Register(Command packageCommand, Func get { var command = new DocumentedCommand("update", Strings.PackageUpdateCommand_Description, "https://aka.ms/dotnet/package/update"); - var packagesArguments = new Argument>("packages") + var packagesArguments = new Argument>("packages") { + Description = Strings.PackageUpdate_PackageArgumentDescription, Arity = ArgumentArity.ZeroOrMore, CustomParser = Package.Parse }; diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateCommandRunner.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateCommandRunner.cs index 75d628d8447..3dca6939463 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateCommandRunner.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/PackageUpdateCommandRunner.cs @@ -267,7 +267,7 @@ private static async Task PreviewUpdatePackageReferenceAsync( newVersion = package.VersionRange; if (newVersion == existingVersion) { - logger.LogMinimal("todo", ConsoleColor.Red); + logger.LogMinimal(Messages.Warning_AlreadyUsingSameVersion(package.Id, newVersion.OriginalString), ConsoleColor.Red); return (null, null); } } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Messages.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Messages.cs index 4173cd5387b..33d95ed6cc3 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Messages.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Messages.cs @@ -31,5 +31,20 @@ internal static string Warning_AlreadyHighestVersion(string packageId, string ve { return string.Format(CultureInfo.CurrentCulture, Strings.Warning_AlreadyHighestVersion, packageId, version, projectPath); } + + internal static string Warning_AlreadyUsingSameVersion(string packageId, string version) + { + return string.Format(CultureInfo.CurrentCulture, Strings.Warning_AlreadyUsingSameVersion, packageId, version); + } + + internal static string Error_MissingVersion(string packageId) + { + return string.Format(CultureInfo.CurrentCulture, Strings.Error_MissingVersion, packageId); + } + + internal static string Error_InvalidVersionRange(string input) + { + return string.Format(CultureInfo.CurrentCulture, Strings.Error_InvalidVersionRange, input); + } } } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs index b11348fc90b..c912387c070 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs @@ -628,6 +628,24 @@ internal static string Error_InvalidSource { } } + /// + /// Looks up a localized string similar to Invalid version range '{0}'. + /// + internal static string Error_InvalidVersionRange { + get { + return ResourceManager.GetString("Error_InvalidVersionRange", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Missing version from {0}. + /// + internal static string Error_MissingVersion { + get { + return ResourceManager.GetString("Error_MissingVersion", resourceCulture); + } + } + /// /// Looks up a localized string similar to MsBuild was unable to open Project '{0}'.. /// @@ -1526,6 +1544,15 @@ internal static string OutputNuGetVersion { } } + /// + /// Looks up a localized string similar to Package reference in the form of a package identifier like 'Newtonsoft.Json' or package identifier and version separated by '@' like 'Newtonsoft.Json@13.0.3'.. + /// + internal static string PackageUpdate_PackageArgumentDescription { + get { + return ResourceManager.GetString("PackageUpdate_PackageArgumentDescription", resourceCulture); + } + } + /// /// Looks up a localized string similar to Preview restore with updated packages was not successful. Force mode is not yet available.. /// @@ -2349,6 +2376,15 @@ internal static string Warning_AlreadyHighestVersion { } } + /// + /// Looks up a localized string similar to Package {0} is already referencing version {1}. + /// + internal static string Warning_AlreadyUsingSameVersion { + get { + return ResourceManager.GetString("Warning_AlreadyUsingSameVersion", resourceCulture); + } + } + /// /// Looks up a localized string similar to Audit source '{0}' did not provide any vulnerability data.. /// diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx index ec46adbafa2..b78078ef47e 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx @@ -1024,4 +1024,20 @@ Do not translate "PackageVersion" Unsupported: Upgrading all packages in a project is not yet supported + + Package {0} is already referencing version {1} + 0 - package id +1 - version string + + + Missing version from {0} + 0 - package id + + + Invalid version range '{0}' + 0 - string provided by customer + + + Package reference in the form of a package identifier like 'Newtonsoft.Json' or package identifier and version separated by '@' like 'Newtonsoft.Json@13.0.3'. + \ No newline at end of file diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageTests.cs index b5c0765e27b..39d1cbd6418 100644 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageTests.cs +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageTests.cs @@ -14,13 +14,13 @@ namespace NuGet.CommandLine.Xplat.Tests.Commands.Package.Update public class PackageTests { private RootCommand _command; - private Argument> _packagesArgument; + private Argument> _packagesArgument; public PackageTests() { _command = new RootCommand(); - _packagesArgument = new Argument>("packages") + _packagesArgument = new Argument>("packages") { Arity = ArgumentArity.ZeroOrMore, CustomParser = Pkg.Parse @@ -51,8 +51,10 @@ public void Parse_TwoPackages_ReturnsListOfTwo() { // Arrange var result = _command.Parse("packageId1 packageId2"); + // Act var packages = result.GetValue(_packagesArgument); + // Assert IReadOnlyList expects = [ new Pkg() { Id = "packageId1", VersionRange = null }, @@ -66,8 +68,10 @@ public void Parse_PackageWithVersion_ReturnsPackageWithVersion() { // Arrange var result = _command.Parse("packageId@1.2.3"); + // Act var packages = result.GetValue(_packagesArgument); + // Assert IReadOnlyList expects = [new Pkg() { @@ -82,8 +86,10 @@ public void Parse_PackageWithRangeSyntax_ReturnsPackageWithVersion() { // Arrange var result = _command.Parse("packageId@[1.2.3,2.0.0)"); + // Act var packages = result.GetValue(_packagesArgument); + // Assert IReadOnlyList expects = [new Pkg() { diff --git a/test/TestUtilities/Test.Utility/TestPackageSpecFactory.cs b/test/TestUtilities/Test.Utility/TestPackageSpecFactory.cs index 66cdbe0cac1..d04a83c997c 100644 --- a/test/TestUtilities/Test.Utility/TestPackageSpecFactory.cs +++ b/test/TestUtilities/Test.Utility/TestPackageSpecFactory.cs @@ -8,6 +8,7 @@ using System.Linq; using NuGet.Commands.Restore; using NuGet.Commands.Restore.Utility; +using NuGet.Common; using NuGet.Configuration; using NuGet.Frameworks; using NuGet.ProjectModel; @@ -21,6 +22,13 @@ public class TestPackageSpecFactory private TestTargetFramework _outerBuild; private Dictionary? _targetFrameworks; + public static string SampleFullPath { get; } = RuntimeEnvironmentHelper.IsWindows ? "c:\\src\\my.csproj" : "/home/me/src/my.csproj"; + + public TestPackageSpecFactory(Action builder) + : this(SampleFullPath, builder) + { + } + public TestPackageSpecFactory(string fullPath, Action builder) : this(fullPath, System.IO.Path.GetDirectoryName(fullPath) ?? throw new ArgumentException(message: "Could not get directory from path", paramName: nameof(fullPath)), builder) { @@ -28,8 +36,8 @@ public TestPackageSpecFactory(string fullPath, Action bu public TestPackageSpecFactory(string fullPath, string directory, Action builder) { - _fullPath = string.IsNullOrWhiteSpace(fullPath) ? throw new ArgumentNullException(nameof(fullPath)) : fullPath; - _directory = string.IsNullOrWhiteSpace(directory) ? throw new ArgumentNullException(nameof(directory)) : directory; + _fullPath = string.IsNullOrWhiteSpace(fullPath) ? throw new ArgumentException("Must not be null or whitespace", nameof(fullPath)) : fullPath; + _directory = string.IsNullOrWhiteSpace(directory) ? throw new ArgumentNullException("Must not be null or whitespace", nameof(directory)) : directory; var tfBuilder = new TargetFrameworkBuilder(); builder(tfBuilder); From 17a46eae24923ca66b1d51fc50d3a23047a0ba07 Mon Sep 17 00:00:00 2001 From: Andy Zivkovic Date: Wed, 11 Jun 2025 12:04:10 +0930 Subject: [PATCH 3/3] fix tests --- .../PackageUpdateCommandRunner.GetPackageToUpdateTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunner.GetPackageToUpdateTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunner.GetPackageToUpdateTests.cs index 5bc9c83ab41..c994cc9358c 100644 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunner.GetPackageToUpdateTests.cs +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunner.GetPackageToUpdateTests.cs @@ -40,7 +40,7 @@ public async Task RequestSinglePackage_GetsRequestedVersion() VersionRange = new VersionRange(new NuGetVersion("1.2.3")) }; - PackageSpec packageSpec = new TestPackageSpecFactory("c:\\src\\my.csproj", builder => + PackageSpec packageSpec = new TestPackageSpecFactory(builder => { builder.WithProperty("TargetFramework", "net9.0") .WithItem("PackageReference", "Contoso.Utils", [new("Version", "1.0.0")]); @@ -76,7 +76,7 @@ public async Task RequestPackageWithoutVersion_GetsLatestVersion() VersionRange = null }; - PackageSpec packageSpec = new TestPackageSpecFactory("c:\\src\\my.csproj", builder => + PackageSpec packageSpec = new TestPackageSpecFactory(builder => { builder.WithProperty("TargetFramework", "net9.0") .WithItem("PackageReference", "Contoso.Utils", [new("Version", "1.0.0")]); @@ -117,7 +117,7 @@ public async Task RequestSinglePackageWithRangeSyntax_GetsRequestedVersion() VersionRange = VersionRange.Parse("[1.2.3,2.0.0)") }; - PackageSpec packageSpec = new TestPackageSpecFactory("c:\\src\\my.csproj", builder => + PackageSpec packageSpec = new TestPackageSpecFactory(builder => { builder.WithProperty("TargetFramework", "net9.0") .WithItem("PackageReference", "Contoso.Utils", [new("Version", "1.0.0")]); @@ -153,7 +153,7 @@ public async Task RequestPackageNotOnSource_LogsError() VersionRange = null }; - PackageSpec packageSpec = new TestPackageSpecFactory("c:\\src\\my.csproj", builder => + PackageSpec packageSpec = new TestPackageSpecFactory(builder => { builder.WithProperty("TargetFramework", "net9.0") .WithItem("PackageReference", "Contoso.Utils", [new("Version", "1.0.0")]);