diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/PackageArgument.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/PackageArgument.cs new file mode 100644 index 00000000000..0377afa2bd4 --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/PackageArgument.cs @@ -0,0 +1,110 @@ +// 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.CommandLine.Parsing; +using System.Diagnostics.CodeAnalysis; + +namespace NuGet.CommandLine.XPlat.Commands.Package +{ + internal record PackageArgument : IEqualityComparer> + { + public required string Id { get; init; } + public required TVersion? Version { get; init; } + internal delegate bool TryParseVersion(string value, out TVersion? version); + private readonly IEqualityComparer _versionComparer; + + public PackageArgument(IEqualityComparer versionComparer) + { + _versionComparer = versionComparer; + } + + public static IReadOnlyList> Parse( + ArgumentResult result, + TryParseVersion parseVersion, + Func getInvalidVersionMessage, + IEqualityComparer versionComparer) + { + ArgumentNullException.ThrowIfNull(result); + ArgumentNullException.ThrowIfNull(parseVersion); + ArgumentNullException.ThrowIfNull(getInvalidVersionMessage); + + if (result.Tokens.Count == 0) + { + return []; + } + + List> packages = new List>(result.Tokens.Count); + + foreach (var token in result.Tokens) + { + string? packageId; + TVersion? newVersion; + int separatorIndex = token.Value.IndexOf('@'); + + if (separatorIndex < 0) + { + packageId = token.Value; + newVersion = default; + } + else + { + packageId = token.Value[..separatorIndex]; + string versionString = token.Value[(separatorIndex + 1)..]; + + if (string.IsNullOrEmpty(versionString)) + { + result.AddError(Messages.Error_MissingVersion(token.Value)); + return []; + } + + if (!parseVersion(versionString, out newVersion)) + { + result.AddError(getInvalidVersionMessage(versionString)); + return []; + } + } + + var package = new PackageArgument(versionComparer) + { + Id = packageId, + Version = newVersion + }; + packages.Add(package); + } + + return packages; + } + + public bool Equals(PackageArgument? x, PackageArgument? y) + { + if (ReferenceEquals(x, y)) + { + return true; + } + + if (x is null || y is null) + { + return false; + } + + if (!x.Id!.Equals(y.Id, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + return _versionComparer.Equals(x.Version, y.Version); + } + + public int GetHashCode([DisallowNull] PackageArgument obj) + { + HashCode hash = new HashCode(); + hash.Add(obj.Id); + hash.Add(obj.Version); + return hash.ToHashCode(); + } + } +} 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 deleted file mode 100644 index fd6cc8ec057..00000000000 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Commands/Package/Update/Package.cs +++ /dev/null @@ -1,93 +0,0 @@ -// 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.CommandLine.Parsing; -using System.Diagnostics.CodeAnalysis; -using NuGet.Versioning; - -namespace NuGet.CommandLine.XPlat.Commands.Package.Update -{ - internal record Package : IEqualityComparer - { - public required string Id { get; init; } - public required VersionRange? VersionRange { get; init; } - - internal static IReadOnlyList 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(Messages.Error_MissingVersion(token.Value)); - return []; - } - if (!VersionRange.TryParse(versionString, out newVersion)) - { - result.AddError(Messages.Error_InvalidVersionRange(versionString)); - return []; - } - } - - var package = new Package - { - Id = packageId, - VersionRange = newVersion - }; - packages.Add(package); - } - - return packages; - } - - public bool Equals(Package? x, Package? y) - { - if (ReferenceEquals(x, y)) - { - return true; - } - - if (x is null || y is null) - { - return false; - } - - if (!x.Id.Equals(y.Id, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - - return VersionRangeComparer.Default.Equals(x.VersionRange, y.VersionRange); - } - - public int GetHashCode([DisallowNull] Package obj) - { - HashCode hash = new HashCode(); - hash.Add(obj.Id, StringComparer.OrdinalIgnoreCase); - hash.Add(obj.VersionRange); - return hash.ToHashCode(); - } - } -} 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 e99e5edfd0c..3bf5f706c37 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 @@ -5,6 +5,7 @@ using System.Collections.Generic; using NuGet.Common; +using NuGet.Versioning; namespace NuGet.CommandLine.XPlat.Commands.Package.Update { @@ -12,7 +13,7 @@ internal record PackageUpdateArgs { public required string Project { get; init; } - public required IReadOnlyList Packages { get; init; } + public required IReadOnlyList> Packages { get; init; } public required bool Interactive { 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 f2e2b12e624..11e684d4504 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 @@ -9,7 +9,9 @@ using System.IO; using System.Threading; using System.Threading.Tasks; +using NuGet.CommandLine.XPlat.Utility; using NuGet.Common; +using NuGet.Versioning; namespace NuGet.CommandLine.XPlat.Commands.Package.Update; @@ -24,11 +26,11 @@ internal static void Register(Command packageCommand, Option interactiveOp { 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 + CustomParser = PackageArgumentParserUtility.ParseWithVersionRange }; command.Arguments.Add(packagesArguments); @@ -49,7 +51,7 @@ internal static void Register(Command packageCommand, Option interactiveOp command.SetAction(async (args, cancellationToken) => { FileSystemInfo? project = args.GetValue(projectOption); - IReadOnlyList packages = args.GetValue(packagesArguments) ?? []; + IReadOnlyList> packages = args.GetValue(packagesArguments) ?? []; bool interactive = args.GetValue(interactiveOption); VerbosityEnum verbosity = args.GetValue(verbosityOption) ?? VerbosityEnum.normal; LogLevel logLevel = verbosity.ToLogLevel(); 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 06015380454..e0235e95312 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 @@ -182,7 +182,7 @@ internal static async Task Run(PackageUpdateArgs args, ILoggerWithColor log } private static async Task<(List vulnerablePackages, int packagesScanned)> SelectVulnerablePackagesToUpdateAsync( - IReadOnlyList? packages, + IReadOnlyList>? packages, DependencyGraphSpec dgSpec, ILoggerWithColor logger, IPackageUpdateIO packageUpdateIO, @@ -283,7 +283,7 @@ bool PackageHasVulnerability(string packageId, NuGetVersion version, IReadOnlyLi } internal static async Task> SelectPackagesToUpdateAsync( - IReadOnlyList packages, + IReadOnlyList> packages, PackageSpec project, ILoggerWithColor logger, IPackageUpdateIO packageUpdateIO, @@ -319,9 +319,9 @@ internal static async Task> SelectPackagesToUpdateAsyn } VersionRange upgradeVersion; - if (package.VersionRange is not null) + if (package.Version is not null) { - upgradeVersion = package.VersionRange; + upgradeVersion = package.Version; if (upgradeVersion == existingVersion) { logger.LogMinimal(Messages.Warning_AlreadyUsingSameVersion(package.Id, upgradeVersion.OriginalString), ConsoleColor.Yellow); @@ -465,8 +465,8 @@ private static (VersionRange? version, List targetFrameworks) } // package.identity.VersionRange is the project's referenced version. - Debug.Assert(package.identity.VersionRange != null); - bool usePrerelease = package.identity.VersionRange.HasLowerBound && package.identity.VersionRange.MinVersion.IsPrerelease; + Debug.Assert(package.identity.Version != null); + bool usePrerelease = package.identity.Version.HasLowerBound && package.identity.Version.MinVersion.IsPrerelease; var latestVersion = await packageUpdateIO.GetLatestVersionAsync(package.identity.Id, usePrerelease, mappedSources, NullLogger.Instance, cancellationToken); if (latestVersion is null) @@ -477,7 +477,7 @@ private static (VersionRange? version, List targetFrameworks) } var upgradeVersion = VersionRange.Parse(latestVersion.OriginalVersion!); - if (upgradeVersion.ToString() == package.identity.VersionRange.ToString()) + if (upgradeVersion.ToString() == package.identity.Version.ToString()) { // Already using the highest version. continue; @@ -488,7 +488,7 @@ private static (VersionRange? version, List targetFrameworks) Package = new PackageToUpdate { Id = package.identity.Id, - CurrentVersion = package.identity.VersionRange, + CurrentVersion = package.identity.Version, NewVersion = upgradeVersion }, TargetFrameworkAliases = package.tfms @@ -499,7 +499,7 @@ private static (VersionRange? version, List targetFrameworks) return successful ? (packagesToUpdate, allProjectPackages.Count) : (null, allProjectPackages.Count); } - private static List<(Package identity, List tfms)>? GetAllPackagesReferencedByProject(PackageSpec project, ILoggerWithColor logger) + private static List<(PackageArgument identity, List tfms)>? GetAllPackagesReferencedByProject(PackageSpec project, ILoggerWithColor logger) { var allPackages = new Dictionary tfms, bool hasError)>(StringComparer.OrdinalIgnoreCase); bool hasErrors = false; @@ -543,10 +543,10 @@ private static (VersionRange? version, List targetFrameworks) return null; } - List<(Package package, List tfms)> result = new(allPackages.Count); + List<(PackageArgument package, List tfms)> result = new(allPackages.Count); foreach (var kvp in allPackages) { - var package = new Package { Id = kvp.Key, VersionRange = kvp.Value.version }; + var package = PackageArgumentFactoryUtility.CreateForVersionRange(kvp.Key, kvp.Value.version); result.Add((package, kvp.Value.tfms)); } diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Messages.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Messages.cs index 4513bc899ce..3675427ac44 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Messages.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Messages.cs @@ -49,6 +49,12 @@ internal static string Error_InvalidVersionRange(string input) return string.Format(CultureInfo.CurrentCulture, Strings.Error_InvalidVersionRange, input); } + /// + internal static string Error_InvalidVersion(string input) + { + return string.Format(CultureInfo.CurrentCulture, Strings.Error_InvalidVersion, input); + } + /// internal static string Error_PackageSourceMappingNotFound(string packageId) { diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs index c29c0d2b84e..166f194d7b2 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs @@ -19,7 +19,7 @@ namespace NuGet.CommandLine.XPlat { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Strings { @@ -673,6 +673,15 @@ internal static string Error_InvalidSource { } } + /// + /// Looks up a localized string similar to Invalid version value `{0}`.. + /// + internal static string Error_InvalidVersion { + get { + return ResourceManager.GetString("Error_InvalidVersion", resourceCulture); + } + } + /// /// Looks up a localized string similar to Invalid version range '{0}'. /// diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx index ae081369f57..a388cdc6af9 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.resx @@ -1104,4 +1104,8 @@ Do not translate "PackageVersion" Argument cannot be null or empty. + + Invalid version value `{0}`. + 0 - An invalid NuGet package version. + \ No newline at end of file diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Utility/PackageArgumentFactoryUtility.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Utility/PackageArgumentFactoryUtility.cs new file mode 100644 index 00000000000..d28562e17b7 --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Utility/PackageArgumentFactoryUtility.cs @@ -0,0 +1,32 @@ +// 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 NuGet.CommandLine.XPlat.Commands.Package; +using NuGet.Versioning; + +namespace NuGet.CommandLine.XPlat.Utility +{ + internal static class PackageArgumentFactoryUtility + { + public static PackageArgument CreateForVersion(string id, NuGetVersion? version) + { + return new PackageArgument((IEqualityComparer)VersionComparer.Default) + { + Id = id, + Version = version + }; + } + + public static PackageArgument CreateForVersionRange(string id, VersionRange? version) + { + return new PackageArgument((IEqualityComparer)VersionRangeComparer.Default) + { + Id = id, + Version = version + }; + } + } +} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/Utility/PackageArgumentParserUtility.cs b/src/NuGet.Core/NuGet.CommandLine.XPlat/Utility/PackageArgumentParserUtility.cs new file mode 100644 index 00000000000..2b0086b581e --- /dev/null +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/Utility/PackageArgumentParserUtility.cs @@ -0,0 +1,33 @@ +// 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.CommandLine.XPlat.Commands.Package; +using NuGet.Versioning; + +namespace NuGet.CommandLine.XPlat.Utility +{ + internal static class PackageArgumentParserUtility + { + public static IReadOnlyList> ParseWithVersion(ArgumentResult result) + { + return PackageArgument.Parse( + result, + NuGetVersion.TryParse, + Messages.Error_InvalidVersion, + (IEqualityComparer)VersionComparer.Default); + } + + public static IReadOnlyList> ParseWithVersionRange(ArgumentResult result) + { + return PackageArgument.Parse( + result, + VersionRange.TryParse, + Messages.Error_InvalidVersionRange, + (IEqualityComparer)VersionRangeComparer.Default); + } + } +} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf index 1a2961fce97..8c379b45c0a 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.cs.xlf @@ -353,6 +353,11 @@ NuGet vyžaduje zdroje HTTPS. Pokud chcete používat zdroje HTTP, musíte v sou Zadaný zdroj {0} není platný. Zadejte platný zdroj. 0 - The invalid source. + + Invalid version value `{0}`. + Invalid version value `{0}`. + 0 - An invalid NuGet package version. + Invalid version range '{0}' Neplatný rozsah verzí {0} diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf index 7db30481336..73f6a0d298a 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.de.xlf @@ -353,6 +353,11 @@ NuGet erfordert HTTPS-Quellen. Um HTTP-Quellen zu verwenden, müssen Sie „allo Die angegebene Quelle "{0}" ist ungültig. Geben Sie eine gültige Quelle an. 0 - The invalid source. + + Invalid version value `{0}`. + Invalid version value `{0}`. + 0 - An invalid NuGet package version. + Invalid version range '{0}' Ungültiger Versionsbereich „{0}“ diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf index 16368281dc0..a47a59f9521 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.es.xlf @@ -353,6 +353,11 @@ NuGet requiere orígenes HTTPS. Para usar orígenes HTTP, es necesario establece El origen "{0}" especificado no es válido. Proporcione un origen válido. 0 - The invalid source. + + Invalid version value `{0}`. + Invalid version value `{0}`. + 0 - An invalid NuGet package version. + Invalid version range '{0}' Intervalo de versiones no válido "{0}" diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf index 9f3113bc24f..76678b8b103 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.fr.xlf @@ -353,6 +353,11 @@ NuGet nécessite des sources HTTPS. Pour utiliser des sources HTTP, vous devez d La source spécifiée '{0}' est non valide. Indiquez une source valide. 0 - The invalid source. + + Invalid version value `{0}`. + Invalid version value `{0}`. + 0 - An invalid NuGet package version. + Invalid version range '{0}' Étendue de version non valide « {0} » diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf index f66f82bc585..83f826fb88c 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.it.xlf @@ -353,6 +353,11 @@ NuGet richiede origini HTTPS. Per utilizzare origini HTTP, è necessario imposta L'origine '{0}' specificata non è valida. Specificare un'origine valida. 0 - The invalid source. + + Invalid version value `{0}`. + Invalid version value `{0}`. + 0 - An invalid NuGet package version. + Invalid version range '{0}' Intervallo versioni non valido '{0}' diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf index 89f094cbac4..18cfe5266d2 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ja.xlf @@ -353,6 +353,11 @@ NuGet には HTTPS ソースが必要です。HTTP ソースを使用するに 指定されたソース '{0}' が正しくありません。有効なソースを指定してください。 0 - The invalid source. + + Invalid version value `{0}`. + Invalid version value `{0}`. + 0 - An invalid NuGet package version. + Invalid version range '{0}' バージョン範囲 '{0}' が無効です diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf index af440ad2ab5..e4e54629225 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ko.xlf @@ -353,6 +353,11 @@ NuGet에는 HTTPS 원본이 필요합니다. HTTP 원본을 사용하려면 NuGe 지정된 소스 '{0}'이(가) 올바르지 않습니다. 유효한 소스를 제공하세요. 0 - The invalid source. + + Invalid version value `{0}`. + Invalid version value `{0}`. + 0 - An invalid NuGet package version. + Invalid version range '{0}' 잘못된 버전 범위 '{0}' diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf index 5293728c52d..6743af692cb 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pl.xlf @@ -353,6 +353,11 @@ Menedżer NuGet wymaga źródeł HTTPS. Aby użyć źródeł HTTP, musisz wyraź Określone źródło „{0}” jest nieprawidłowe. Określ prawidłowe źródło. 0 - The invalid source. + + Invalid version value `{0}`. + Invalid version value `{0}`. + 0 - An invalid NuGet package version. + Invalid version range '{0}' Nieprawidłowy zakres wersji „{0}” diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf index 3b1e582a6d2..0b65852a4c4 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.pt-BR.xlf @@ -353,6 +353,11 @@ O NuGet requer fontes HTTPS. Para usar fontes HTTP, você deve definir explicita A origem especificada '{0}' é inválida. Forneça uma origem válida. 0 - The invalid source. + + Invalid version value `{0}`. + Invalid version value `{0}`. + 0 - An invalid NuGet package version. + Invalid version range '{0}' Intervalo de versão "{0}" inválido diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf index 71438cec309..75c7be53c10 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.ru.xlf @@ -353,6 +353,11 @@ NuGet requires HTTPS sources. To use HTTP sources, you must explicitly set 'allo Указанный источник "{0}" является недопустимым. Укажите допустимый источник. 0 - The invalid source. + + Invalid version value `{0}`. + Invalid version value `{0}`. + 0 - An invalid NuGet package version. + Invalid version range '{0}' Недопустимый диапазон версий "{0}" diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf index 02b92de82fe..041fc4ec0f8 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.tr.xlf @@ -354,6 +354,11 @@ NuGet için HTTPS kaynakları gereklidir. HTTP kaynaklarını kullanmak için Nu Belirtilen '{0}' kaynağı geçersiz. Geçerli bir kaynak belirtin. 0 - The invalid source. + + Invalid version value `{0}`. + Invalid version value `{0}`. + 0 - An invalid NuGet package version. + Invalid version range '{0}' Geçersiz sürüm aralığı '{0}' diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf index 1de6f867783..6cd9fd4e869 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hans.xlf @@ -353,6 +353,11 @@ NuGet 需要 HTTPS 源。要使用 HTTP 源,必须在 NuGet.Config 文件中 指定的源“{0}”无效。请提供有效的源。 0 - The invalid source. + + Invalid version value `{0}`. + Invalid version value `{0}`. + 0 - An invalid NuGet package version. + Invalid version range '{0}' 版本范围“{0}”无效 diff --git a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf index 8649718f00d..fd63bd7f226 100644 --- a/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf +++ b/src/NuGet.Core/NuGet.CommandLine.XPlat/xlf/Strings.zh-Hant.xlf @@ -353,6 +353,11 @@ NuGet 需要 HTTPS 來源。您必須在 NuGet.Config 檔案中將 'allowInsecur 指定的來源 '{0}' 無效。請提供有效的來源。 0 - The invalid source. + + Invalid version value `{0}`. + Invalid version value `{0}`. + 0 - An invalid NuGet package version. + Invalid version range '{0}' 不正確版本範圍 '{0}' diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/PackageArgumentTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/PackageArgumentTests.cs new file mode 100644 index 00000000000..b2bc72c6515 --- /dev/null +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/PackageArgumentTests.cs @@ -0,0 +1,202 @@ +using System; +using System.Collections.Generic; +using System.CommandLine; +using System.CommandLine.Parsing; +using FluentAssertions; +using NuGet.CommandLine.XPlat.Commands.Package; +using Xunit; + +namespace NuGet.CommandLine.Xplat.Tests.Commands.Package +{ + public class PackageArgumentTests + { + private static readonly IEqualityComparer DefaultVersionComparer = EqualityComparer.Default; + private static readonly Func DefaultErrorFactory = v => "Invalid version: " + v; + + private static (IReadOnlyList> Packages, IReadOnlyList Errors) + ParsePackages( + string input, + PackageArgument.TryParseVersion tryParseVersion, + IEqualityComparer versionComparer = null, + Func errorFactory = null) + { + versionComparer ??= DefaultVersionComparer; + errorFactory ??= DefaultErrorFactory; + + var root = new RootCommand(); + var arg = new Argument>>("package") + { + Arity = ArgumentArity.ZeroOrMore, + CustomParser = (ArgumentResult r) => PackageArgument.Parse( + r, + tryParseVersion, + errorFactory, + versionComparer) + }; + root.Arguments.Add(arg); + + var result = root.Parse(input); + var packages = result.GetValue(arg); + return (packages, result.Errors); + } + + private static PackageArgument Package(string id, string version, IEqualityComparer cmp = null) + { + cmp ??= DefaultVersionComparer; + var package = new PackageArgument(cmp) + { + Id = id, + Version = version + }; + return package; + } + + // A permissive parser used by several tests + private static bool PassThrough(string value, out string version) + { + version = value; + return true; + } + + [Theory] + [InlineData("package")] + [InlineData("Package")] + [InlineData("my.cool.pkg")] + public void Parse_IdOnlyNoVersion_SetsNullVersion(string id) + { + // Arrange & Act + var (packages, errors) = ParsePackages(id, PassThrough); + + // Assert + packages.Should().BeEquivalentTo( + [ + Package(id, null) + ]); + errors.Should().BeEmpty(); + } + + [Fact] + public void Parse_MultiplePackages_ParsesAll() + { + // Arrange & Act + var (packages, errors) = ParsePackages("A@1.0.0 B C@[2,3) d@(,5]", PassThrough); + + // Assert + packages.Should().BeEquivalentTo( + [ + Package("A", "1.0.0"), + Package("B", null), + Package("C", "[2,3)"), + Package("d", "(,5]") + ]); + errors.Should().BeEmpty(); + } + + [Theory] + [InlineData("pkg@")] + [InlineData("x@ ")] + public void Parse_MissingOrEmptyVersion_AddsError(string input) + { + // Arrange + static bool RejectWhitespace(string value, out string version) + { + if (string.IsNullOrWhiteSpace(value)) + { + version = default; + return false; + } + version = value; + return true; + } + + // Act & Assert + var result = Assert.Throws(() => ParsePackages(input, RejectWhitespace)); + result.Message.Should().Contain("Missing version"); + } + + [Fact] + public void Parse_InvalidVersion_AddsError() + { + // Arrange + string version = "v1.2.3"; + static bool DigitsOnly(string value, out string version) + { + version = default; + return false; + } + + // Act & Assert + var result = Assert.Throws(() => ParsePackages($"pkg@{version}", DigitsOnly)); + result.Message.Should().Be(DefaultErrorFactory(version)); + } + + [Theory] + [InlineData("pkg@1.2.3", "1.2.3-normalized")] + [InlineData("pkg@[1,3)", "[1,3)-normalized")] + public void Parse_ActionParser_CanTransformVersion(string input, string normalized) + { + // Arrange + static bool Normalize(string value, out string version) + { + version = value + "-normalized"; + return true; + } + + // Act & Assert + var (packages, errors) = ParsePackages(input, Normalize); + + packages.Should().BeEquivalentTo( + [ + Package("pkg", normalized) + ]); + errors.Should().BeEmpty(); + } + + [Fact] + public void EqualityComparer_IsCaseInsensitiveOnId_AndUsesProvidedVersionComparer() + { + // Arrange + var firstCharacterComparer = EqualityComparer.Create( + (a, b) => + { + if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b)) + { + return a == b; + } + + return a[0] == b[0]; + }, + v => + { + if (string.IsNullOrEmpty(v)) + { + return 0; + } + + return v[0].GetHashCode(); + }); + + var eq = new PackageArgument(firstCharacterComparer) { Id = null, Version = null }; + + var a = new PackageArgument(firstCharacterComparer) { Id = "Foo", Version = "1.0.0" }; + var b = new PackageArgument(firstCharacterComparer) { Id = "foo", Version = "1.0.0+meta" }; + var c = new PackageArgument(firstCharacterComparer) { Id = "bar", Version = "1.0.0" }; + var d = new PackageArgument(firstCharacterComparer) { Id = "bar", Version = "2.0.0" }; + + // Act & Assert + eq.Equals(a, b).Should().BeTrue(); + eq.Equals(a, c).Should().BeFalse(); + eq.Equals(c, d).Should().BeFalse(); + } + + [Theory] + [InlineData("package", "1.2.3")] + [InlineData("package", "(1,2)")] + [InlineData("package", "[1,3]")] + public void Parse_NoActionParser_ReturnsTheSameVersion(string id, string version) + { + var (packages, _) = ParsePackages(id + "@" + version, PassThrough); + packages.Should().BeEquivalentTo([Package(id, version)]); + } + } +} diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/CliParserTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/CliParserTests.cs index 3e5f936abea..8a6fa9fe9de 100644 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/CliParserTests.cs +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/CliParserTests.cs @@ -48,7 +48,7 @@ public async Task WithSinglePackage_ShouldParsePackageId() result.Should().NotBeNull(); result.Packages.Should().HaveCount(1); result.Packages[0].Id.Should().Be("Contoso.Utils"); - result.Packages[0].VersionRange.Should().BeNull(); + result.Packages[0].Version.Should().BeNull(); } [Fact] @@ -64,9 +64,9 @@ public async Task WithMultiplePackages_ShouldParseAllPackageIds() result.Should().NotBeNull(); result.Packages.Should().HaveCount(2); result.Packages[0].Id.Should().Be("Contoso.Utils"); - result.Packages[0].VersionRange.Should().BeNull(); + result.Packages[0].Version.Should().BeNull(); result.Packages[1].Id.Should().Be("Contoso.Framework"); - result.Packages[1].VersionRange.Should().BeNull(); + result.Packages[1].Version.Should().BeNull(); } [Fact] @@ -82,8 +82,8 @@ public async Task WithPackageAndVersion_ShouldParsePackageWithVersionRange() result.Should().NotBeNull(); result.Packages.Should().HaveCount(1); result.Packages[0].Id.Should().Be("Contoso.Utils"); - result.Packages[0].VersionRange.Should().NotBeNull(); - result.Packages[0].VersionRange!.ToString().Should().Be("[2.1.0, )"); + result.Packages[0].Version.Should().NotBeNull(); + result.Packages[0].Version!.ToString().Should().Be("[2.1.0, )"); } [Fact] @@ -99,8 +99,8 @@ public async Task WithPackageAndVersionRange_ShouldParsePackageWithVersionRange( result.Should().NotBeNull(); result.Packages.Should().HaveCount(1); result.Packages[0].Id.Should().Be("Contoso.Utils"); - result.Packages[0].VersionRange.Should().NotBeNull(); - result.Packages[0].VersionRange!.ToString().Should().Be("[2.0.0, 3.0.0)"); + result.Packages[0].Version.Should().NotBeNull(); + result.Packages[0].Version!.ToString().Should().Be("[2.0.0, 3.0.0)"); } [Fact] @@ -116,9 +116,9 @@ public async Task WithMixedPackages_ShouldParseMixedPackagesCorrectly() result.Should().NotBeNull(); result.Packages.Should().HaveCount(2); result.Packages[0].Id.Should().Be("Contoso.Utils"); - result.Packages[0].VersionRange.Should().NotBeNull(); + result.Packages[0].Version.Should().NotBeNull(); result.Packages[1].Id.Should().Be("Contoso.Framework"); - result.Packages[1].VersionRange.Should().BeNull(); + result.Packages[1].Version.Should().BeNull(); } [Fact] @@ -217,7 +217,7 @@ public async Task WithAllOptions_ShouldParseAllOptionsCorrectly() result.Should().NotBeNull(); result.Packages.Should().HaveCount(1); result.Packages[0].Id.Should().Be("Contoso.Utils"); - result.Packages[0].VersionRange.Should().NotBeNull(); + result.Packages[0].Version.Should().NotBeNull(); result.Project.Should().Be(projectPath); result.Interactive.Should().BeTrue(); result.LogLevel.Should().Be(LogLevel.Verbose); 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 deleted file mode 100644 index 39d1cbd6418..00000000000 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageTests.cs +++ /dev/null @@ -1,122 +0,0 @@ -// 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/PackageUpdateCommandRunnerTests/GetPackageToUpdateTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunnerTests/GetPackageToUpdateTests.cs index 26c6e8c901c..6cd8acdfcd8 100644 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunnerTests/GetPackageToUpdateTests.cs +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunnerTests/GetPackageToUpdateTests.cs @@ -11,6 +11,7 @@ using Moq; using NuGet.CommandLine.XPlat; using NuGet.CommandLine.XPlat.Commands.Package.Update; +using NuGet.CommandLine.XPlat.Utility; using NuGet.Common; using NuGet.Configuration; using NuGet.ProjectModel; @@ -22,7 +23,7 @@ namespace NuGet.CommandLine.Xplat.Tests.Commands.Package.Update.PackageUpdateCommandRunnerTests; -using Pkg = NuGet.CommandLine.XPlat.Commands.Package.Update.Package; +using Pkg = NuGet.CommandLine.XPlat.Commands.Package.PackageArgument; public class GetPackageToUpdateTests { @@ -40,11 +41,7 @@ public GetPackageToUpdateTests(ITestOutputHelper output) public async Task RequestSinglePackage_GetsRequestedVersion() { // Arrange - Pkg package = new() - { - Id = "Contoso.Utils", - VersionRange = new VersionRange(new NuGetVersion("1.2.3")) - }; + Pkg package = PackageArgumentFactoryUtility.CreateForVersionRange("Contoso.Utils", new VersionRange(new NuGetVersion("1.2.3"))); PackageSpec packageSpec = new TestPackageSpecFactory(builder => { @@ -80,11 +77,7 @@ public async Task RequestSinglePackage_GetsRequestedVersion() public async Task RequestPackageWithoutVersion_GetsLatestVersion() { // Arrange - Pkg package = new() - { - Id = "Contoso.Utils", - VersionRange = null - }; + Pkg package = PackageArgumentFactoryUtility.CreateForVersionRange("Contoso.Utils", null); PackageSpec packageSpec = new TestPackageSpecFactory(builder => { @@ -124,11 +117,7 @@ public async Task RequestPackageWithoutVersion_GetsLatestVersion() public async Task RequestSinglePackageWithRangeSyntax_GetsRequestedVersion() { // Arrange - Pkg package = new() - { - Id = "Contoso.Utils", - VersionRange = VersionRange.Parse("[1.2.3,2.0.0)") - }; + Pkg package = PackageArgumentFactoryUtility.CreateForVersionRange("Contoso.Utils", VersionRange.Parse("[1.2.3,2.0.0)")); PackageSpec packageSpec = new TestPackageSpecFactory(builder => { @@ -164,11 +153,7 @@ public async Task RequestSinglePackageWithRangeSyntax_GetsRequestedVersion() public async Task RequestPackageNotOnSource_LogsError() { // Arrange - Pkg package = new() - { - Id = "Contoso.Utils", - VersionRange = null - }; + Pkg package = PackageArgumentFactoryUtility.CreateForVersionRange("Contoso.Utils", null); PackageSpec packageSpec = new TestPackageSpecFactory(builder => { @@ -202,17 +187,9 @@ public async Task RequestPackageNotOnSource_LogsError() public async Task RequestMultiplePackages_GetsAllRequested() { // Arrange - Pkg package1 = new() - { - Id = "Contoso.Utils", - VersionRange = new VersionRange(new NuGetVersion("1.2.3")) - }; + Pkg package1 = PackageArgumentFactoryUtility.CreateForVersionRange("Contoso.Utils", new VersionRange(new NuGetVersion("1.2.3"))); - Pkg package2 = new() - { - Id = "Fabrikam.Tools", - VersionRange = null // Should get latest version - }; + Pkg package2 = PackageArgumentFactoryUtility.CreateForVersionRange("Fabrikam.Tools", null); PackageSpec packageSpec = new TestPackageSpecFactory(builder => { @@ -256,11 +233,7 @@ public async Task RequestMultiplePackages_GetsAllRequested() public async Task RequestPackageUpdateNotReferencedByProject_ReturnsEmpty() { // Arrange - Pkg package = new() - { - Id = "NotReferenced.Package", - VersionRange = new VersionRange(new NuGetVersion("1.0.0")) - }; + Pkg package = PackageArgumentFactoryUtility.CreateForVersionRange("NotReferenced.Package", new VersionRange(new NuGetVersion("1.0.0"))); PackageSpec packageSpec = new TestPackageSpecFactory(builder => { diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunnerTests/MultiProjectTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunnerTests/MultiProjectTests.cs index 117b1316b24..3e3fe331305 100644 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunnerTests/MultiProjectTests.cs +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunnerTests/MultiProjectTests.cs @@ -16,10 +16,11 @@ using NuGet.Versioning; using Test.Utility; using Xunit; +using NuGet.CommandLine.XPlat.Utility; namespace NuGet.CommandLine.Xplat.Tests.Commands.Package.Update.PackageUpdateCommandRunnerTests; -using Pkg = XPlat.Commands.Package.Update.Package; +using Pkg = XPlat.Commands.Package.PackageArgument; public class MultiProjectTests { @@ -51,7 +52,7 @@ public async Task ProjectWithP2PReference_SinglePackage_UpdatesCorrectPackageVer var packagesToUpdate = new List { - new Pkg { Id = "Test.Package", VersionRange = new VersionRange(new NuGetVersion("1.2.3")) } + PackageArgumentFactoryUtility.CreateForVersionRange("Test.Package", new VersionRange(new NuGetVersion("1.2.3"))) }; TestData testData = InitTest(project1Path, packagesToUpdate, dgSpec); diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunnerTests/SingleProjectTests.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunnerTests/SingleProjectTests.cs index 75e198e8580..b82ab2a695f 100644 --- a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunnerTests/SingleProjectTests.cs +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Commands/Package/Update/PackageUpdateCommandRunnerTests/SingleProjectTests.cs @@ -15,10 +15,11 @@ using NuGet.Versioning; using Test.Utility; using Xunit; +using NuGet.CommandLine.XPlat.Utility; namespace NuGet.CommandLine.Xplat.Tests.Commands.Package.Update.PackageUpdateCommandRunnerTests; -using Pkg = XPlat.Commands.Package.Update.Package; +using Pkg = XPlat.Commands.Package.PackageArgument; public class SingleProjectTests { @@ -34,7 +35,7 @@ public async Task SingleTarget_SinglePackage_UpdatesCorrectPackageVersion() var packagesToUpdate = new List { - new Pkg { Id = "Test.Package", VersionRange = new VersionRange(new NuGetVersion("1.2.3")) } + PackageArgumentFactoryUtility.CreateForVersionRange("Test.Package", new VersionRange(new NuGetVersion("1.2.3"))) }; TestData testData = InitTest(packagesToUpdate, packageSpec); @@ -72,8 +73,8 @@ public async Task SingleTarget_MultiplePackages_UpdatesBothPackages() var packagesToUpdate = new List { - new Pkg { Id = "Test.Package1", VersionRange = new VersionRange(new NuGetVersion("1.2.3")) }, - new Pkg { Id = "Test.Package2", VersionRange = new VersionRange(new NuGetVersion("2.1.0")) } + PackageArgumentFactoryUtility.CreateForVersionRange("Test.Package1", new VersionRange(new NuGetVersion("1.2.3"))), + PackageArgumentFactoryUtility.CreateForVersionRange("Test.Package2", new VersionRange(new NuGetVersion("2.1.0"))) }; TestData testData = InitTest(packagesToUpdate, packageSpec); @@ -127,7 +128,7 @@ public async Task MultiTargetingWithConditionalPackage_SinglePackage_UpdatesExpe var packagesToUpdate = new List { - new Pkg { Id = "Contoso.Polyfill", VersionRange = new VersionRange(new NuGetVersion("1.2.3")) } + PackageArgumentFactoryUtility.CreateForVersionRange("Contoso.Polyfill", new VersionRange(new NuGetVersion("1.2.3"))) }; TestData testData = InitTest(packagesToUpdate, packageSpec); @@ -174,7 +175,7 @@ public async Task MultiTargetingWithTfmAliases_SinglePackage_UpdatesExpectedPack var packagesToUpdate = new List { - new Pkg { Id = "Contoso.Polyfill", VersionRange = new VersionRange(new NuGetVersion("1.2.3")) } + PackageArgumentFactoryUtility.CreateForVersionRange("Contoso.Polyfill", new VersionRange(new NuGetVersion("1.2.3"))) }; TestData testData = InitTest(packagesToUpdate, packageSpec); @@ -204,7 +205,7 @@ public async Task PackageNotReferenced_ReturnsErrorExitCode() }).Build(); var packagesToUpdate = new List { - new Pkg { Id = "Non.Existent.Package", VersionRange = new VersionRange(new NuGetVersion("1.2.3")) } + PackageArgumentFactoryUtility.CreateForVersionRange("Non.Existent.Package", new VersionRange(new NuGetVersion("1.2.3"))) }; TestData testData = InitTest(packagesToUpdate, packageSpec); @@ -238,7 +239,7 @@ public async Task PreviewRestoreFails_ProjectIsNotUpdated() }).Build(); var packagesToUpdate = new List { - new Pkg { Id = "Test.Package", VersionRange = new VersionRange(new NuGetVersion("1.2.3")) } + PackageArgumentFactoryUtility.CreateForVersionRange("Test.Package", new VersionRange(new NuGetVersion("1.2.3"))) }; TestData testData = InitTest(packagesToUpdate, packageSpec, restoreSuccessful: false); @@ -270,7 +271,7 @@ public async Task ProjectCantBeLoaded_ReturnsErrorExitCode() Project = "nonexistent.csproj", Packages = new List { - new Pkg { Id = "Test.Package", VersionRange = new VersionRange(new NuGetVersion("1.2.3")) } + PackageArgumentFactoryUtility.CreateForVersionRange("Test.Package", new VersionRange(new NuGetVersion("1.2.3"))) }, Interactive = false, LogLevel = LogLevel.Information, @@ -344,7 +345,7 @@ public async Task ProjectWithPackageSourceMapping_ErrorWhenPackageNameIsNotMappe { Packages = new List { - new Pkg { Id = "Test.Package", VersionRange = null } + PackageArgumentFactoryUtility.CreateForVersionRange("Test.Package", null) } } }; @@ -394,7 +395,7 @@ public async Task ProjectWithPackageSourceMapping_PassesMappedSourcesToGetLatest { Packages = new List { - new Pkg { Id = "Test.Package", VersionRange = null } + PackageArgumentFactoryUtility.CreateForVersionRange("Test.Package", null) } } }; diff --git a/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Utility/PackageArgumentParserUtilityTest.cs b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Utility/PackageArgumentParserUtilityTest.cs new file mode 100644 index 00000000000..1c7e9fc35ae --- /dev/null +++ b/test/NuGet.Core.Tests/NuGet.CommandLine.Xplat.Tests/Utility/PackageArgumentParserUtilityTest.cs @@ -0,0 +1,229 @@ +// 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 System.Linq; +using FluentAssertions; +using NuGet.Versioning; +using Xunit; +using PackageWithVersion = NuGet.CommandLine.XPlat.Commands.Package.PackageArgument; +using PackageWithVersionRange = NuGet.CommandLine.XPlat.Commands.Package.PackageArgument; + +namespace NuGet.CommandLine.Xplat.Tests.Utility +{ + public class PackageArgumentParserUtilityTest + { + private readonly RootCommand _versionRangeCommand; + private readonly Argument> _versionRangePackagesArgument; + private readonly RootCommand _versionCommand; + private readonly Argument> _versionPackagesArgument; + + public PackageArgumentParserUtilityTest() + { + _versionRangeCommand = new RootCommand(); + + _versionRangePackagesArgument = new Argument>("packages") + { + Arity = ArgumentArity.ZeroOrMore, + CustomParser = XPlat.Utility.PackageArgumentParserUtility.ParseWithVersionRange + }; + + _versionRangeCommand.Arguments.Add(_versionRangePackagesArgument); + + _versionCommand = new RootCommand(); + _versionPackagesArgument = new Argument>("packages") + { + Arity = ArgumentArity.ZeroOrMore, + CustomParser = XPlat.Utility.PackageArgumentParserUtility.ParseWithVersion + }; + _versionCommand.Arguments.Add(_versionPackagesArgument); + } + + [Fact] + public void Parse_OnePackage_ReturnsListOfOne() + { + // Arrange + var result = _versionRangeCommand.Parse("packageId"); + + // Act + var packages = result.GetValue(_versionRangePackagesArgument); + + // Assert + packages.First().Id.Should().Be("packageId"); + packages.First().Version.Should().BeNull(); + } + + [Fact] + public void Parse_TwoPackages_ReturnsListOfTwo() + { + // Arrange + var result = _versionRangeCommand.Parse("packageId1 packageId2"); + + // Act + var packages = result.GetValue(_versionRangePackagesArgument); + + // Assert + packages.Count.Should().Be(2); + packages[0].Id.Should().Be("packageId1"); + packages[0].Version.Should().BeNull(); + packages[1].Id.Should().Be("packageId2"); + packages[1].Version.Should().BeNull(); + } + + [Fact] + public void Parse_PackageWithVersion_ReturnsPackageWithVersion() + { + // Arrange + var result = _versionRangeCommand.Parse("packageId@1.2.3"); + + // Act + var packages = result.GetValue(_versionRangePackagesArgument); + + // Assert + packages.Count.Should().Be(1); + packages[0].Id.Should().Be("packageId"); + packages[0].Version.Should().Be(VersionRange.Parse("1.2.3")); + } + + [Fact] + public void Parse_PackageWithRangeSyntax_ReturnsPackageWithVersion() + { + // Arrange + var result = _versionRangeCommand.Parse("packageId@[1.2.3,2.0.0)"); + + // Act + var packages = result.GetValue(_versionRangePackagesArgument); + + // Assert + packages.Count.Should().Be(1); + packages[0].Id.Should().Be("packageId"); + packages[0].Version.Should().Be(VersionRange.Parse("[1.2.3,2.0.0)")); + } + + [Fact] + public void Parse_VersionWithNoId_ReturnsError() + { + // Arrange & Act + var result = _versionRangeCommand.Parse("@1.2.3"); + + // Assert + result.Errors.Should().ContainSingle(); + } + + [Fact] + public void Parse_PackageWithInvalidVersion_ReturnsError() + { + // Arrange & Act + var result = _versionRangeCommand.Parse("packageId@one"); + + // Assert + result.Errors.Should().ContainSingle(); + } + + [Fact] + public void Parse_PackageWithExactVersion_ReturnsPackageWithExactVersion() + { + // Arrange + var result = _versionCommand.Parse("packageId@1.2.3"); + + // Act + var packages = result.GetValue(_versionPackagesArgument); + + // Assert + packages.Count.Should().Be(1); + packages[0].Id.Should().Be("packageId"); + packages[0].Version.Should().Be(new NuGetVersion("1.2.3")); + } + + [Fact] + public void Parse_TwoPackagesWithExactVersions_ReturnsListOfTwo() + { + // Arrange + var result = _versionCommand.Parse("packageId1@1.0.0 packageId2@2.3.4"); + + // Act + var packages = result.GetValue(_versionPackagesArgument); + + // Assert + packages.Count.Should().Be(2); + packages[0].Id.Should().Be("packageId1"); + packages[0].Version.Should().Be(new NuGetVersion("1.0.0")); + packages[1].Id.Should().Be("packageId2"); + packages[1].Version.Should().Be(new NuGetVersion("2.3.4")); + } + + [Fact] + public void Parse_PackageWithoutVersion_ReturnsPackageWithNullVersion() + { + // Arrange & Act + var result = _versionCommand.Parse("packageId"); + + // Act + var packages = result.GetValue(_versionPackagesArgument); + + // Assert + packages.Count.Should().Be(1); + packages[0].Id.Should().Be("packageId"); + packages[0].Version.Should().BeNull(); + } + + [Fact] + public void Parse_ExactVersionWithNoId_ReturnsError() + { + // Arrange & Act + var result = _versionCommand.Parse("@1.2.3"); + + // Assert + result.Errors.Should().ContainSingle(); + } + + [Fact] + public void Parse_PackageWithRangeSyntax_ReturnsError() + { + // Arrange & Act + var result = _versionCommand.Parse("packageId@[1.2.3,2.0.0)"); + + // Assert + result.Errors.Should().ContainSingle(); + } + + [Fact] + public void Parse_PackageWithInvalidExactVersion_ReturnsError() + { + // Arrange & Act + var result = _versionCommand.Parse("packageId@one"); + + // Assert + result.Errors.Should().ContainSingle(); + } + + [Fact] + public void Equal_SameIdAndVersion_AreEqual() + { + // Arrange + var package1 = _versionCommand.Parse("packageId@1.2.3").GetValue(_versionPackagesArgument).First(); + var package2 = _versionCommand.Parse("packageId@1.2.3").GetValue(_versionPackagesArgument).First(); + + // Act + var areEqual = package1.Equals(package1, package2); + + // Assert + areEqual.Should().BeTrue(); + } + + [Fact] + public void Equal_DifferentId_AreNotEqual() + { + // Arrange + var package1 = _versionCommand.Parse("packageId1@1.2.3").GetValue(_versionPackagesArgument).First(); + var package2 = _versionCommand.Parse("packageId2@1.2.3").GetValue(_versionPackagesArgument).First(); + + // Act + var areEqual = package1.Equals(package1, package2); + + // Assert + areEqual.Should().BeFalse(); + } + } +}