From 5ebbd618ec27e5c6da9142b8c1166b2755cba898 Mon Sep 17 00:00:00 2001 From: Peter Huene Date: Thu, 22 Feb 2018 19:13:36 -0800 Subject: [PATCH] Fix case sensitivity of tool package identifiers. This commit fixes the case sensitivity of tool package identifiers. Previously the install and uninstall commands unintentionally required the tool package ids to specified in all lowercase for the install / uninstall to work. Fixes #8682. --- src/dotnet/CommonLocalizableStrings.resx | 3 + src/dotnet/ToolPackage/IToolPackage.cs | 5 +- .../ToolPackage/IToolPackageInstaller.cs | 5 +- src/dotnet/ToolPackage/IToolPackageStore.cs | 15 +- src/dotnet/ToolPackage/PackageId.cs | 43 +++++ .../ToolPackage/ToolPackageInstaller.cs | 43 ++--- src/dotnet/ToolPackage/ToolPackageInstance.cs | 33 ++-- src/dotnet/ToolPackage/ToolPackageStore.cs | 84 +++++++-- .../dotnet-install-tool/InstallToolCommand.cs | 23 ++- .../LocalizableStrings.resx | 3 + .../xlf/LocalizableStrings.cs.xlf | 5 + .../xlf/LocalizableStrings.de.xlf | 5 + .../xlf/LocalizableStrings.es.xlf | 5 + .../xlf/LocalizableStrings.fr.xlf | 5 + .../xlf/LocalizableStrings.it.xlf | 5 + .../xlf/LocalizableStrings.ja.xlf | 5 + .../xlf/LocalizableStrings.ko.xlf | 5 + .../xlf/LocalizableStrings.pl.xlf | 5 + .../xlf/LocalizableStrings.pt-BR.xlf | 5 + .../xlf/LocalizableStrings.ru.xlf | 5 + .../xlf/LocalizableStrings.tr.xlf | 5 + .../xlf/LocalizableStrings.zh-Hans.xlf | 5 + .../xlf/LocalizableStrings.zh-Hant.xlf | 5 + .../dotnet-list-tool/ListToolCommand.cs | 10 +- .../tool/UninstallToolCommand.cs | 9 +- .../xlf/CommonLocalizableStrings.cs.xlf | 5 + .../xlf/CommonLocalizableStrings.de.xlf | 5 + .../xlf/CommonLocalizableStrings.es.xlf | 5 + .../xlf/CommonLocalizableStrings.fr.xlf | 5 + .../xlf/CommonLocalizableStrings.it.xlf | 5 + .../xlf/CommonLocalizableStrings.ja.xlf | 5 + .../xlf/CommonLocalizableStrings.ko.xlf | 5 + .../xlf/CommonLocalizableStrings.pl.xlf | 5 + .../xlf/CommonLocalizableStrings.pt-BR.xlf | 5 + .../xlf/CommonLocalizableStrings.ru.xlf | 5 + .../xlf/CommonLocalizableStrings.tr.xlf | 5 + .../xlf/CommonLocalizableStrings.zh-Hans.xlf | 5 + .../xlf/CommonLocalizableStrings.zh-Hant.xlf | 5 + .../ToolPackageInstallerTests.cs | 91 ++++++---- .../ProjectRestorerMock.cs | 27 +-- .../ToolPackageInstallerMock.cs | 26 ++- .../ToolPackageMock.cs | 17 +- .../ToolPackageStoreMock.cs | 77 +++++++- .../CommandTests/InstallToolCommandTests.cs | 164 +++++++++++++++--- 44 files changed, 629 insertions(+), 179 deletions(-) create mode 100644 src/dotnet/ToolPackage/PackageId.cs diff --git a/src/dotnet/CommonLocalizableStrings.resx b/src/dotnet/CommonLocalizableStrings.resx index 77847a864..236b2fd26 100644 --- a/src/dotnet/CommonLocalizableStrings.resx +++ b/src/dotnet/CommonLocalizableStrings.resx @@ -619,6 +619,9 @@ setx PATH "%PATH%;{0}" Tool '{0}' (version '{1}') is already installed. + + Failed to find staged tool package '{0}'. + Column maximum width must be greater than zero. diff --git a/src/dotnet/ToolPackage/IToolPackage.cs b/src/dotnet/ToolPackage/IToolPackage.cs index 3ee968cb9..c769b002d 100644 --- a/src/dotnet/ToolPackage/IToolPackage.cs +++ b/src/dotnet/ToolPackage/IToolPackage.cs @@ -4,14 +4,15 @@ using System; using System.Collections.Generic; using Microsoft.Extensions.EnvironmentAbstractions; +using NuGet.Versioning; namespace Microsoft.DotNet.ToolPackage { internal interface IToolPackage { - string PackageId { get; } + PackageId Id { get; } - string PackageVersion { get; } + NuGetVersion Version { get; } DirectoryPath PackageDirectory { get; } diff --git a/src/dotnet/ToolPackage/IToolPackageInstaller.cs b/src/dotnet/ToolPackage/IToolPackageInstaller.cs index d8501d22d..c6ef964e1 100644 --- a/src/dotnet/ToolPackage/IToolPackageInstaller.cs +++ b/src/dotnet/ToolPackage/IToolPackageInstaller.cs @@ -4,14 +4,15 @@ using System; using System.Collections.Generic; using Microsoft.Extensions.EnvironmentAbstractions; +using NuGet.Versioning; namespace Microsoft.DotNet.ToolPackage { internal interface IToolPackageInstaller { IToolPackage InstallPackage( - string packageId, - string packageVersion = null, + PackageId packageId, + VersionRange versionRange = null, string targetFramework = null, FilePath? nugetConfig = null, string source = null, diff --git a/src/dotnet/ToolPackage/IToolPackageStore.cs b/src/dotnet/ToolPackage/IToolPackageStore.cs index 72105d24d..4e3e88c65 100644 --- a/src/dotnet/ToolPackage/IToolPackageStore.cs +++ b/src/dotnet/ToolPackage/IToolPackageStore.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using Microsoft.Extensions.EnvironmentAbstractions; +using NuGet.Versioning; namespace Microsoft.DotNet.ToolPackage { @@ -11,6 +12,18 @@ namespace Microsoft.DotNet.ToolPackage { DirectoryPath Root { get; } - IEnumerable GetInstalledPackages(string packageId = null); + DirectoryPath GetRandomStagingDirectory(); + + NuGetVersion GetStagedPackageVersion(DirectoryPath stagingDirectory, PackageId packageId); + + DirectoryPath GetRootPackageDirectory(PackageId packageId); + + DirectoryPath GetPackageDirectory(PackageId packageId, NuGetVersion version); + + IEnumerable EnumeratePackages(); + + IEnumerable EnumeratePackageVersions(PackageId packageId); + + IToolPackage GetPackage(PackageId packageId, NuGetVersion version); } } diff --git a/src/dotnet/ToolPackage/PackageId.cs b/src/dotnet/ToolPackage/PackageId.cs new file mode 100644 index 000000000..07846bf7a --- /dev/null +++ b/src/dotnet/ToolPackage/PackageId.cs @@ -0,0 +1,43 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using Microsoft.DotNet.InternalAbstractions; + +namespace Microsoft.DotNet.ToolPackage +{ + internal struct PackageId : IEquatable, IComparable + { + private string _id; + + public PackageId(string id) + { + _id = id?.ToLowerInvariant() ?? throw new ArgumentNullException(nameof(id)); + } + + public bool Equals(PackageId other) + { + return ToString() == other.ToString(); + } + + public int CompareTo(PackageId other) + { + return string.Compare(ToString(), other.ToString(), StringComparison.Ordinal); + } + + public override bool Equals(object obj) + { + return obj is PackageId id && Equals(id); + } + + public override int GetHashCode() + { + return ToString().GetHashCode(); + } + + public override string ToString() + { + return _id ?? ""; + } + } +} diff --git a/src/dotnet/ToolPackage/ToolPackageInstaller.cs b/src/dotnet/ToolPackage/ToolPackageInstaller.cs index e30a4cc7e..cf655a301 100644 --- a/src/dotnet/ToolPackage/ToolPackageInstaller.cs +++ b/src/dotnet/ToolPackage/ToolPackageInstaller.cs @@ -7,13 +7,12 @@ using Microsoft.DotNet.Cli; using Microsoft.DotNet.Configurer; using Microsoft.DotNet.Tools; using Microsoft.Extensions.EnvironmentAbstractions; +using NuGet.Versioning; namespace Microsoft.DotNet.ToolPackage { internal class ToolPackageInstaller : IToolPackageInstaller { - public const string StagingDirectory = ".stage"; - private readonly IToolPackageStore _store; private readonly IProjectRestorer _projectRestorer; private readonly FilePath? _tempProject; @@ -32,33 +31,27 @@ namespace Microsoft.DotNet.ToolPackage } public IToolPackage InstallPackage( - string packageId, - string packageVersion = null, + PackageId packageId, + VersionRange versionRange = null, string targetFramework = null, FilePath? nugetConfig = null, string source = null, string verbosity = null) { - if (packageId == null) - { - throw new ArgumentNullException(nameof(packageId)); - } - - var packageRootDirectory = _store.Root.WithSubDirectories(packageId); + var packageRootDirectory = _store.GetRootPackageDirectory(packageId); string rollbackDirectory = null; return TransactionalAction.Run( action: () => { try { - - var stageDirectory = _store.Root.WithSubDirectories(StagingDirectory, Path.GetRandomFileName()); + var stageDirectory = _store.GetRandomStagingDirectory(); Directory.CreateDirectory(stageDirectory.Value); rollbackDirectory = stageDirectory.Value; var tempProject = CreateTempProject( packageId: packageId, - packageVersion: packageVersion, + versionRange: versionRange, targetFramework: targetFramework ?? BundledTargetFramework.GetTargetFrameworkMoniker(), restoreDirectory: stageDirectory); @@ -76,29 +69,22 @@ namespace Microsoft.DotNet.ToolPackage File.Delete(tempProject.Value); } - packageVersion = Path.GetFileName( - Directory.EnumerateDirectories( - stageDirectory.WithSubDirectories(packageId).Value).Single()); - - var packageDirectory = packageRootDirectory.WithSubDirectories(packageVersion); + var version = _store.GetStagedPackageVersion(stageDirectory, packageId); + var packageDirectory = _store.GetPackageDirectory(packageId, version); if (Directory.Exists(packageDirectory.Value)) { throw new ToolPackageException( string.Format( CommonLocalizableStrings.ToolPackageConflictPackageId, packageId, - packageVersion)); + version.ToNormalizedString())); } Directory.CreateDirectory(packageRootDirectory.Value); Directory.Move(stageDirectory.Value, packageDirectory.Value); rollbackDirectory = packageDirectory.Value; - return new ToolPackageInstance( - _store, - packageId, - packageVersion, - packageDirectory); + return new ToolPackageInstance(_store, packageId, version, packageDirectory); } catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException) { @@ -126,8 +112,8 @@ namespace Microsoft.DotNet.ToolPackage } private FilePath CreateTempProject( - string packageId, - string packageVersion, + PackageId packageId, + VersionRange versionRange, string targetFramework, DirectoryPath restoreDirectory) { @@ -159,8 +145,9 @@ namespace Microsoft.DotNet.ToolPackage new XElement("DisableImplicitNuGetFallbackFolder", "true")), // disable SDK side implicit NuGetFallbackFolder new XElement("ItemGroup", new XElement("PackageReference", - new XAttribute("Include", packageId), - new XAttribute("Version", packageVersion ?? "*") // nuget will restore * for latest + new XAttribute("Include", packageId.ToString()), + new XAttribute("Version", + versionRange?.ToString("S", new VersionRangeFormatter()) ?? "*") // nuget will restore latest stable for * )) )); diff --git a/src/dotnet/ToolPackage/ToolPackageInstance.cs b/src/dotnet/ToolPackage/ToolPackageInstance.cs index 5841c2ef6..74b092109 100644 --- a/src/dotnet/ToolPackage/ToolPackageInstance.cs +++ b/src/dotnet/ToolPackage/ToolPackageInstance.cs @@ -6,6 +6,7 @@ using Microsoft.DotNet.Cli; using Microsoft.DotNet.Tools; using Microsoft.Extensions.EnvironmentAbstractions; using NuGet.ProjectModel; +using NuGet.Versioning; namespace Microsoft.DotNet.ToolPackage { @@ -17,20 +18,21 @@ namespace Microsoft.DotNet.ToolPackage public ToolPackageInstance( IToolPackageStore store, - string packageId, - string packageVersion, + PackageId id, + NuGetVersion version, DirectoryPath packageDirectory) { _store = store ?? throw new ArgumentNullException(nameof(store)); - PackageId = packageId ?? throw new ArgumentNullException(nameof(packageId)); - PackageVersion = packageVersion ?? throw new ArgumentNullException(nameof(packageVersion)); - PackageDirectory = packageDirectory; _commands = new Lazy>(GetCommands); + + Id = id; + Version = version ?? throw new ArgumentNullException(nameof(version)); + PackageDirectory = packageDirectory; } - public string PackageId { get; private set; } + public PackageId Id { get; private set; } - public string PackageVersion { get; private set; } + public NuGetVersion Version { get; private set; } public DirectoryPath PackageDirectory { get; private set; } @@ -53,13 +55,9 @@ namespace Microsoft.DotNet.ToolPackage { if (Directory.Exists(PackageDirectory.Value)) { - // Use the same staging directory for uninstall instead of temp + // Use the staging directory for uninstall // This prevents cross-device moves when temp is mounted to a different device - var tempPath = _store - .Root - .WithSubDirectories(ToolPackageInstaller.StagingDirectory) - .WithFile(Path.GetRandomFileName()) - .Value; + var tempPath = _store.GetRandomStagingDirectory().Value; Directory.Move(PackageDirectory.Value, tempPath); tempPackageDirectory = tempPath; } @@ -75,7 +73,7 @@ namespace Microsoft.DotNet.ToolPackage throw new ToolPackageException( string.Format( CommonLocalizableStrings.FailedToUninstallToolPackage, - PackageId, + Id, ex.Message), ex); } @@ -116,7 +114,7 @@ namespace Microsoft.DotNet.ToolPackage var toolConfigurationPath = PackageDirectory .WithSubDirectories( - PackageId, + Id.ToString(), library.Version.ToNormalizedString()) .WithFile(dotnetToolSettings.Path); @@ -138,7 +136,7 @@ namespace Microsoft.DotNet.ToolPackage "dotnet", PackageDirectory .WithSubDirectories( - PackageId, + Id.ToString(), library.Version.ToNormalizedString()) .WithFile(entryPointFromLockFile.Path))); @@ -158,7 +156,8 @@ namespace Microsoft.DotNet.ToolPackage { return lockFile ?.Targets?.SingleOrDefault(t => t.RuntimeIdentifier != null) - ?.Libraries?.SingleOrDefault(l => l.Name == PackageId); + ?.Libraries?.SingleOrDefault(l => + string.Compare(l.Name, Id.ToString(), StringComparison.CurrentCultureIgnoreCase) == 0); } private static LockFileItem FindItemInTargetLibrary(LockFileTargetLibrary library, string targetRelativeFilePath) diff --git a/src/dotnet/ToolPackage/ToolPackageStore.cs b/src/dotnet/ToolPackage/ToolPackageStore.cs index f9575a9cc..f424b6062 100644 --- a/src/dotnet/ToolPackage/ToolPackageStore.cs +++ b/src/dotnet/ToolPackage/ToolPackageStore.cs @@ -2,12 +2,16 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using Microsoft.DotNet.Tools; using Microsoft.Extensions.EnvironmentAbstractions; +using NuGet.Versioning; namespace Microsoft.DotNet.ToolPackage { internal class ToolPackageStore : IToolPackageStore { + public const string StagingDirectory = ".stage"; + public ToolPackageStore(DirectoryPath root) { Root = root; @@ -15,17 +19,45 @@ namespace Microsoft.DotNet.ToolPackage public DirectoryPath Root { get; private set; } - public IEnumerable GetInstalledPackages(string packageId = null) + public DirectoryPath GetRandomStagingDirectory() { - if (packageId != null) - { - return EnumerateVersions(packageId); - } - - return EnumerateAllPackages().SelectMany(p => p); + return Root.WithSubDirectories(StagingDirectory, Path.GetRandomFileName()); } - private IEnumerable> EnumerateAllPackages() + public NuGetVersion GetStagedPackageVersion(DirectoryPath stagingDirectory, PackageId packageId) + { + if (NuGetVersion.TryParse( + Path.GetFileName( + Directory.EnumerateDirectories( + stagingDirectory.WithSubDirectories(packageId.ToString()).Value).FirstOrDefault()), + out var version)) + { + return version; + } + + throw new ToolPackageException( + string.Format( + CommonLocalizableStrings.FailedToFindStagedToolPackage, + packageId)); + } + + public DirectoryPath GetRootPackageDirectory(PackageId packageId) + { + return Root.WithSubDirectories(packageId.ToString()); + } + + public DirectoryPath GetPackageDirectory(PackageId packageId, NuGetVersion version) + { + if (version == null) + { + throw new ArgumentNullException(nameof(version)); + } + + return GetRootPackageDirectory(packageId) + .WithSubDirectories(version.ToNormalizedString().ToLowerInvariant()); + } + + public IEnumerable EnumeratePackages() { if (!Directory.Exists(Root.Value)) { @@ -34,19 +66,25 @@ namespace Microsoft.DotNet.ToolPackage foreach (var subdirectory in Directory.EnumerateDirectories(Root.Value)) { - var packageId = Path.GetFileName(subdirectory); - if (packageId == ToolPackageInstaller.StagingDirectory) + var name = Path.GetFileName(subdirectory); + var packageId = new PackageId(name); + + // Ignore the staging directory and any directory that isn't the same as the package id + if (name == StagingDirectory || name != packageId.ToString()) { continue; } - yield return EnumerateVersions(packageId); + foreach (var package in EnumeratePackageVersions(packageId)) + { + yield return package; + } } } - private IEnumerable EnumerateVersions(string packageId) + public IEnumerable EnumeratePackageVersions(PackageId packageId) { - var packageRootDirectory = Root.WithSubDirectories(packageId); + var packageRootDirectory = Root.WithSubDirectories(packageId.ToString()); if (!Directory.Exists(packageRootDirectory.Value)) { yield break; @@ -54,13 +92,27 @@ namespace Microsoft.DotNet.ToolPackage foreach (var subdirectory in Directory.EnumerateDirectories(packageRootDirectory.Value)) { - var version = Path.GetFileName(subdirectory); yield return new ToolPackageInstance( this, packageId, - version, - packageRootDirectory.WithSubDirectories(version)); + NuGetVersion.Parse(Path.GetFileName(subdirectory)), + new DirectoryPath(subdirectory)); } } + + public IToolPackage GetPackage(PackageId packageId, NuGetVersion version) + { + if (version == null) + { + throw new ArgumentNullException(nameof(version)); + } + + var directory = GetPackageDirectory(packageId, version); + if (!Directory.Exists(directory.Value)) + { + return null; + } + return new ToolPackageInstance(this, packageId, version, directory); + } } } diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/InstallToolCommand.cs b/src/dotnet/commands/dotnet-install/dotnet-install-tool/InstallToolCommand.cs index 881f81491..8459c2b55 100644 --- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/InstallToolCommand.cs +++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/InstallToolCommand.cs @@ -13,6 +13,7 @@ using Microsoft.DotNet.Configurer; using Microsoft.DotNet.ShellShim; using Microsoft.DotNet.ToolPackage; using Microsoft.Extensions.EnvironmentAbstractions; +using NuGet.Versioning; namespace Microsoft.DotNet.Tools.Install.Tool { @@ -25,7 +26,7 @@ namespace Microsoft.DotNet.Tools.Install.Tool private readonly IReporter _reporter; private readonly IReporter _errorReporter; - private readonly string _packageId; + private readonly PackageId _packageId; private readonly string _packageVersion; private readonly string _configFilePath; private readonly string _framework; @@ -48,7 +49,7 @@ namespace Microsoft.DotNet.Tools.Install.Tool throw new ArgumentNullException(nameof(appliedCommand)); } - _packageId = appliedCommand.Arguments.Single(); + _packageId = new PackageId(appliedCommand.Arguments.Single()); _packageVersion = appliedCommand.ValueOrDefault("version"); _configFilePath = appliedCommand.ValueOrDefault("configfile"); _framework = appliedCommand.ValueOrDefault("framework"); @@ -91,8 +92,16 @@ namespace Microsoft.DotNet.Tools.Install.Tool Path.GetFullPath(_configFilePath))); } - // Prevent installation if any version of the package is installed - if (_toolPackageStore.GetInstalledPackages(_packageId).FirstOrDefault() != null) + VersionRange versionRange = null; + if (!string.IsNullOrEmpty(_packageVersion) && !VersionRange.TryParse(_packageVersion, out versionRange)) + { + throw new GracefulException( + string.Format( + LocalizableStrings.InvalidNuGetVersionRange, + _packageVersion)); + } + + if (_toolPackageStore.EnumeratePackageVersions(_packageId).FirstOrDefault() != null) { _errorReporter.WriteLine(string.Format(LocalizableStrings.ToolAlreadyInstalled, _packageId).Red()); return 1; @@ -113,7 +122,7 @@ namespace Microsoft.DotNet.Tools.Install.Tool { package = _toolPackageInstaller.InstallPackage( packageId: _packageId, - packageVersion: _packageVersion, + versionRange: versionRange, targetFramework: _framework, nugetConfig: configFile, source: _source, @@ -133,8 +142,8 @@ namespace Microsoft.DotNet.Tools.Install.Tool string.Format( LocalizableStrings.InstallationSucceeded, string.Join(", ", package.Commands.Select(c => c.Name)), - package.PackageId, - package.PackageVersion).Green()); + package.Id, + package.Version.ToNormalizedString()).Green()); return 0; } catch (ToolPackageException ex) diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/LocalizableStrings.resx b/src/dotnet/commands/dotnet-install/dotnet-install-tool/LocalizableStrings.resx index 8d366537c..2b463ae96 100644 --- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/LocalizableStrings.resx +++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/LocalizableStrings.resx @@ -178,4 +178,7 @@ Tool '{1}' (version '{2}') was successfully installed. Failed to create shell shim for tool '{0}': {1} + + Specified version '{0}' is not a valid NuGet version range. + \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.cs.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.cs.xlf index eb8dae6d6..894f345b3 100644 --- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.cs.xlf +++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.cs.xlf @@ -104,6 +104,11 @@ Instalace byla úspěšná. Pokud nejsou další pokyny, můžete přímo do já Balíček nástroje nebylo možné obnovit. + + Specified version '{0}' is not a valid NuGet version range. + Specified version '{0}' is not a valid NuGet version range. + + \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.de.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.de.xlf index 941d48da4..2d551c8f0 100644 --- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.de.xlf +++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.de.xlf @@ -104,6 +104,11 @@ Die Installation war erfolgreich. Sofern keine weiteren Anweisungen vorliegen, k Das Toolpaket konnte nicht wiederhergestellt werden. + + Specified version '{0}' is not a valid NuGet version range. + Specified version '{0}' is not a valid NuGet version range. + + \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.es.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.es.xlf index 13656cbc9..1907042c2 100644 --- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.es.xlf +++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.es.xlf @@ -104,6 +104,11 @@ La instalación se completó correctamente. Si no hay más instrucciones, puede No se puede restaurar el paquete de la herramienta. + + Specified version '{0}' is not a valid NuGet version range. + Specified version '{0}' is not a valid NuGet version range. + + \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.fr.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.fr.xlf index 65a0f7bcd..217f901ca 100644 --- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.fr.xlf +++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.fr.xlf @@ -104,6 +104,11 @@ L'installation a réussi. En l'absence d'instructions supplémentaires, vous pou Impossible de restaurer le package de l'outil. + + Specified version '{0}' is not a valid NuGet version range. + Specified version '{0}' is not a valid NuGet version range. + + \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.it.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.it.xlf index 9ffb72dfb..69df9674e 100644 --- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.it.xlf +++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.it.xlf @@ -104,6 +104,11 @@ L'installazione è riuscita. Se non ci sono altre istruzioni, è possibile digit Non è stato possibile ripristinare il pacchetto dello strumento. + + Specified version '{0}' is not a valid NuGet version range. + Specified version '{0}' is not a valid NuGet version range. + + \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ja.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ja.xlf index dd81fbb48..eb86d3b58 100644 --- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ja.xlf +++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ja.xlf @@ -104,6 +104,11 @@ Tool '{1}' (version '{2}') was successfully installed. ツール パッケージを復元できませんでした。 + + Specified version '{0}' is not a valid NuGet version range. + Specified version '{0}' is not a valid NuGet version range. + + \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ko.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ko.xlf index 79acf23a0..41b63aff0 100644 --- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ko.xlf +++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ko.xlf @@ -104,6 +104,11 @@ Tool '{1}' (version '{2}') was successfully installed. 도구 패키지를 복원할 수 없습니다. + + Specified version '{0}' is not a valid NuGet version range. + Specified version '{0}' is not a valid NuGet version range. + + \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.pl.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.pl.xlf index 95e8b774f..c77c195ab 100644 --- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.pl.xlf +++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.pl.xlf @@ -104,6 +104,11 @@ Instalacja powiodła się. Jeśli nie ma dodatkowych instrukcji, możesz wpisać Nie można przywrócić pakietu narzędzia. + + Specified version '{0}' is not a valid NuGet version range. + Specified version '{0}' is not a valid NuGet version range. + + \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.pt-BR.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.pt-BR.xlf index d70b28da7..ab6c7b086 100644 --- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.pt-BR.xlf +++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.pt-BR.xlf @@ -104,6 +104,11 @@ A instalação foi bem-sucedida. Se não houver outras instruções, digite o se O pacote da ferramenta não pôde ser restaurado. + + Specified version '{0}' is not a valid NuGet version range. + Specified version '{0}' is not a valid NuGet version range. + + \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ru.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ru.xlf index a3705f15d..d4b0de225 100644 --- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ru.xlf +++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.ru.xlf @@ -104,6 +104,11 @@ Tool '{1}' (version '{2}') was successfully installed. Не удалось восстановить пакет инструмента. + + Specified version '{0}' is not a valid NuGet version range. + Specified version '{0}' is not a valid NuGet version range. + + \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.tr.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.tr.xlf index a9dd17bb6..13ed942cf 100644 --- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.tr.xlf +++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.tr.xlf @@ -104,6 +104,11 @@ Yükleme başarılı oldu. Daha fazla yönerge yoksa, çağırmak için şu komu Araç paketi geri yüklenemedi. + + Specified version '{0}' is not a valid NuGet version range. + Specified version '{0}' is not a valid NuGet version range. + + \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.zh-Hans.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.zh-Hans.xlf index 840ebd25a..29f352687 100644 --- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.zh-Hans.xlf +++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.zh-Hans.xlf @@ -104,6 +104,11 @@ Tool '{1}' (version '{2}') was successfully installed. 无法还原工具包。 + + Specified version '{0}' is not a valid NuGet version range. + Specified version '{0}' is not a valid NuGet version range. + + \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.zh-Hant.xlf b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.zh-Hant.xlf index 1a75879a6..911527d54 100644 --- a/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.zh-Hant.xlf +++ b/src/dotnet/commands/dotnet-install/dotnet-install-tool/xlf/LocalizableStrings.zh-Hant.xlf @@ -104,6 +104,11 @@ Tool '{1}' (version '{2}') was successfully installed. 此工具套件無法還原。 + + Specified version '{0}' is not a valid NuGet version range. + Specified version '{0}' is not a valid NuGet version range. + + \ No newline at end of file diff --git a/src/dotnet/commands/dotnet-list/dotnet-list-tool/ListToolCommand.cs b/src/dotnet/commands/dotnet-list/dotnet-list-tool/ListToolCommand.cs index b732ba3a8..b567ac74d 100644 --- a/src/dotnet/commands/dotnet-list/dotnet-list-tool/ListToolCommand.cs +++ b/src/dotnet/commands/dotnet-list/dotnet-list-tool/ListToolCommand.cs @@ -46,10 +46,10 @@ namespace Microsoft.DotNet.Tools.List.Tool table.AddColumn( LocalizableStrings.PackageIdColumn, - p => p.PackageId); + p => p.Id.ToString()); table.AddColumn( LocalizableStrings.VersionColumn, - p => p.PackageVersion); + p => p.Version.ToNormalizedString()); table.AddColumn( LocalizableStrings.CommandsColumn, p => string.Join(CommandDelimiter, p.Commands.Select(c => c.Name))); @@ -60,9 +60,9 @@ namespace Microsoft.DotNet.Tools.List.Tool private IEnumerable GetPackages() { - return _toolPackageStore.GetInstalledPackages() + return _toolPackageStore.EnumeratePackages() .Where(PackageHasCommands) - .OrderBy(p => p.PackageId) + .OrderBy(p => p.Id) .ToArray(); } @@ -79,7 +79,7 @@ namespace Microsoft.DotNet.Tools.List.Tool _errorReporter.WriteLine( string.Format( LocalizableStrings.InvalidPackageWarning, - p.PackageId, + p.Id, ex.Message).Yellow()); return false; } diff --git a/src/dotnet/commands/dotnet-uninstall/tool/UninstallToolCommand.cs b/src/dotnet/commands/dotnet-uninstall/tool/UninstallToolCommand.cs index d362e3838..ca470f4a6 100644 --- a/src/dotnet/commands/dotnet-uninstall/tool/UninstallToolCommand.cs +++ b/src/dotnet/commands/dotnet-uninstall/tool/UninstallToolCommand.cs @@ -49,12 +49,11 @@ namespace Microsoft.DotNet.Tools.Uninstall.Tool throw new GracefulException(LocalizableStrings.UninstallToolCommandOnlySupportsGlobal); } - var packageId = _options.Arguments.Single(); + var packageId = new PackageId(_options.Arguments.Single()); IToolPackage package = null; - try { - package = _toolPackageStore.GetInstalledPackages(packageId).SingleOrDefault(); + package = _toolPackageStore.EnumeratePackageVersions(packageId).SingleOrDefault(); if (package == null) { _errorReporter.WriteLine( @@ -92,8 +91,8 @@ namespace Microsoft.DotNet.Tools.Uninstall.Tool _reporter.WriteLine( string.Format( LocalizableStrings.UninstallSucceeded, - package.PackageId, - package.PackageVersion).Green()); + package.Id, + package.Version.ToNormalizedString()).Green()); return 0; } catch (ToolPackageException ex) diff --git a/src/dotnet/xlf/CommonLocalizableStrings.cs.xlf b/src/dotnet/xlf/CommonLocalizableStrings.cs.xlf index 8f0e7c10c..70be5e60b 100644 --- a/src/dotnet/xlf/CommonLocalizableStrings.cs.xlf +++ b/src/dotnet/xlf/CommonLocalizableStrings.cs.xlf @@ -853,6 +853,11 @@ setx PATH "%PATH%;{0}" Settings file 'DotnetToolSettings.xml' was not found in the package. + + Failed to find staged tool package '{0}'. + Failed to find staged tool package '{0}'. + + \ No newline at end of file diff --git a/src/dotnet/xlf/CommonLocalizableStrings.de.xlf b/src/dotnet/xlf/CommonLocalizableStrings.de.xlf index 3a93064c3..6ef10330e 100644 --- a/src/dotnet/xlf/CommonLocalizableStrings.de.xlf +++ b/src/dotnet/xlf/CommonLocalizableStrings.de.xlf @@ -853,6 +853,11 @@ setx PATH "%PATH%;{0}" Settings file 'DotnetToolSettings.xml' was not found in the package. + + Failed to find staged tool package '{0}'. + Failed to find staged tool package '{0}'. + + \ No newline at end of file diff --git a/src/dotnet/xlf/CommonLocalizableStrings.es.xlf b/src/dotnet/xlf/CommonLocalizableStrings.es.xlf index f283122ee..c9d3df366 100644 --- a/src/dotnet/xlf/CommonLocalizableStrings.es.xlf +++ b/src/dotnet/xlf/CommonLocalizableStrings.es.xlf @@ -853,6 +853,11 @@ setx PATH "%PATH%;{0}" Settings file 'DotnetToolSettings.xml' was not found in the package. + + Failed to find staged tool package '{0}'. + Failed to find staged tool package '{0}'. + + \ No newline at end of file diff --git a/src/dotnet/xlf/CommonLocalizableStrings.fr.xlf b/src/dotnet/xlf/CommonLocalizableStrings.fr.xlf index 3a668e77f..ab0d6ae1f 100644 --- a/src/dotnet/xlf/CommonLocalizableStrings.fr.xlf +++ b/src/dotnet/xlf/CommonLocalizableStrings.fr.xlf @@ -853,6 +853,11 @@ setx PATH "%PATH%;{0}" Settings file 'DotnetToolSettings.xml' was not found in the package. + + Failed to find staged tool package '{0}'. + Failed to find staged tool package '{0}'. + + \ No newline at end of file diff --git a/src/dotnet/xlf/CommonLocalizableStrings.it.xlf b/src/dotnet/xlf/CommonLocalizableStrings.it.xlf index 89d2e6f1e..6210b184f 100644 --- a/src/dotnet/xlf/CommonLocalizableStrings.it.xlf +++ b/src/dotnet/xlf/CommonLocalizableStrings.it.xlf @@ -853,6 +853,11 @@ setx PATH "%PATH%;{0}" Settings file 'DotnetToolSettings.xml' was not found in the package. + + Failed to find staged tool package '{0}'. + Failed to find staged tool package '{0}'. + + \ No newline at end of file diff --git a/src/dotnet/xlf/CommonLocalizableStrings.ja.xlf b/src/dotnet/xlf/CommonLocalizableStrings.ja.xlf index 5d42dbf93..4541550f0 100644 --- a/src/dotnet/xlf/CommonLocalizableStrings.ja.xlf +++ b/src/dotnet/xlf/CommonLocalizableStrings.ja.xlf @@ -853,6 +853,11 @@ setx PATH "%PATH%;{0}" Settings file 'DotnetToolSettings.xml' was not found in the package. + + Failed to find staged tool package '{0}'. + Failed to find staged tool package '{0}'. + + \ No newline at end of file diff --git a/src/dotnet/xlf/CommonLocalizableStrings.ko.xlf b/src/dotnet/xlf/CommonLocalizableStrings.ko.xlf index e136f3d5c..c9d93cce8 100644 --- a/src/dotnet/xlf/CommonLocalizableStrings.ko.xlf +++ b/src/dotnet/xlf/CommonLocalizableStrings.ko.xlf @@ -853,6 +853,11 @@ setx PATH "%PATH%;{0}" Settings file 'DotnetToolSettings.xml' was not found in the package. + + Failed to find staged tool package '{0}'. + Failed to find staged tool package '{0}'. + + \ No newline at end of file diff --git a/src/dotnet/xlf/CommonLocalizableStrings.pl.xlf b/src/dotnet/xlf/CommonLocalizableStrings.pl.xlf index 0a2d70abc..2b22b4c8c 100644 --- a/src/dotnet/xlf/CommonLocalizableStrings.pl.xlf +++ b/src/dotnet/xlf/CommonLocalizableStrings.pl.xlf @@ -853,6 +853,11 @@ setx PATH "%PATH%;{0}" Settings file 'DotnetToolSettings.xml' was not found in the package. + + Failed to find staged tool package '{0}'. + Failed to find staged tool package '{0}'. + + \ No newline at end of file diff --git a/src/dotnet/xlf/CommonLocalizableStrings.pt-BR.xlf b/src/dotnet/xlf/CommonLocalizableStrings.pt-BR.xlf index 81b666a10..60b4b7863 100644 --- a/src/dotnet/xlf/CommonLocalizableStrings.pt-BR.xlf +++ b/src/dotnet/xlf/CommonLocalizableStrings.pt-BR.xlf @@ -853,6 +853,11 @@ setx PATH "%PATH%;{0}" Settings file 'DotnetToolSettings.xml' was not found in the package. + + Failed to find staged tool package '{0}'. + Failed to find staged tool package '{0}'. + + \ No newline at end of file diff --git a/src/dotnet/xlf/CommonLocalizableStrings.ru.xlf b/src/dotnet/xlf/CommonLocalizableStrings.ru.xlf index 0926eee52..50ee839d2 100644 --- a/src/dotnet/xlf/CommonLocalizableStrings.ru.xlf +++ b/src/dotnet/xlf/CommonLocalizableStrings.ru.xlf @@ -853,6 +853,11 @@ setx PATH "%PATH%;{0}" Settings file 'DotnetToolSettings.xml' was not found in the package. + + Failed to find staged tool package '{0}'. + Failed to find staged tool package '{0}'. + + \ No newline at end of file diff --git a/src/dotnet/xlf/CommonLocalizableStrings.tr.xlf b/src/dotnet/xlf/CommonLocalizableStrings.tr.xlf index 3d316c24f..a20c55ce0 100644 --- a/src/dotnet/xlf/CommonLocalizableStrings.tr.xlf +++ b/src/dotnet/xlf/CommonLocalizableStrings.tr.xlf @@ -853,6 +853,11 @@ setx PATH "%PATH%;{0}" Settings file 'DotnetToolSettings.xml' was not found in the package. + + Failed to find staged tool package '{0}'. + Failed to find staged tool package '{0}'. + + \ No newline at end of file diff --git a/src/dotnet/xlf/CommonLocalizableStrings.zh-Hans.xlf b/src/dotnet/xlf/CommonLocalizableStrings.zh-Hans.xlf index b18d951b8..d26ed195d 100644 --- a/src/dotnet/xlf/CommonLocalizableStrings.zh-Hans.xlf +++ b/src/dotnet/xlf/CommonLocalizableStrings.zh-Hans.xlf @@ -853,6 +853,11 @@ setx PATH "%PATH%;{0}" Settings file 'DotnetToolSettings.xml' was not found in the package. + + Failed to find staged tool package '{0}'. + Failed to find staged tool package '{0}'. + + \ No newline at end of file diff --git a/src/dotnet/xlf/CommonLocalizableStrings.zh-Hant.xlf b/src/dotnet/xlf/CommonLocalizableStrings.zh-Hant.xlf index 8c61b8d28..fc901551e 100644 --- a/src/dotnet/xlf/CommonLocalizableStrings.zh-Hant.xlf +++ b/src/dotnet/xlf/CommonLocalizableStrings.zh-Hant.xlf @@ -853,6 +853,11 @@ setx PATH "%PATH%;{0}" Settings file 'DotnetToolSettings.xml' was not found in the package. + + Failed to find staged tool package '{0}'. + Failed to find staged tool package '{0}'. + + \ No newline at end of file diff --git a/test/Microsoft.DotNet.ToolPackage.Tests/ToolPackageInstallerTests.cs b/test/Microsoft.DotNet.ToolPackage.Tests/ToolPackageInstallerTests.cs index 9c4bf61f5..46268c106 100644 --- a/test/Microsoft.DotNet.ToolPackage.Tests/ToolPackageInstallerTests.cs +++ b/test/Microsoft.DotNet.ToolPackage.Tests/ToolPackageInstallerTests.cs @@ -16,6 +16,7 @@ using Microsoft.DotNet.Tools.Install.Tool; using Microsoft.DotNet.Tools.Tests.ComponentMocks; using Microsoft.Extensions.DependencyModel.Tests; using Microsoft.Extensions.EnvironmentAbstractions; +using NuGet.Versioning; using Xunit; namespace Microsoft.DotNet.ToolPackage.Tests @@ -33,13 +34,13 @@ namespace Microsoft.DotNet.ToolPackage.Tests Action a = () => installer.InstallPackage( packageId: TestPackageId, - packageVersion: TestPackageVersion, + versionRange: VersionRange.Parse(TestPackageVersion), targetFramework: _testTargetframework); a.ShouldThrow().WithMessage(LocalizableStrings.ToolInstallationRestoreFailed); reporter.Lines.Count.Should().Be(1); - reporter.Lines[0].Should().Contain(TestPackageId); + reporter.Lines[0].Should().Contain(TestPackageId.ToString()); } [Theory] @@ -54,7 +55,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests var package = installer.InstallPackage( packageId: TestPackageId, - packageVersion: TestPackageVersion, + versionRange: VersionRange.Parse(TestPackageVersion), targetFramework: _testTargetframework); AssertPackageInstall(reporter, fileSystem, package, store); @@ -75,7 +76,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests var package = installer.InstallPackage( packageId: TestPackageId, - packageVersion: TestPackageVersion, + versionRange: VersionRange.Parse(TestPackageVersion), targetFramework: _testTargetframework, nugetConfig: nugetConfigPath); @@ -102,7 +103,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests { package = installer.InstallPackage( packageId: TestPackageId, - packageVersion: TestPackageVersion, + versionRange: VersionRange.Parse(TestPackageVersion), targetFramework: _testTargetframework, nugetConfig: nugetConfigPath); @@ -127,7 +128,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests var package = installer.InstallPackage( packageId: TestPackageId, - packageVersion: TestPackageVersion, + versionRange: VersionRange.Parse(TestPackageVersion), targetFramework: _testTargetframework, nugetConfig: nugetConfigPath); @@ -166,7 +167,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests var package = installer.InstallPackage( packageId: TestPackageId, - packageVersion: TestPackageVersion, + versionRange: VersionRange.Parse(TestPackageVersion), targetFramework: _testTargetframework); AssertPackageInstall(reporter, fileSystem, package, store); @@ -208,7 +209,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests var package = installer.InstallPackage( packageId: TestPackageId, - packageVersion: TestPackageVersion, + versionRange: VersionRange.Parse(TestPackageVersion), nugetConfig: nugetConfigPath); AssertPackageInstall(reporter, fileSystem, package, store); @@ -229,7 +230,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests var package = installer.InstallPackage( packageId: TestPackageId, - packageVersion: TestPackageVersion, + versionRange: VersionRange.Parse(TestPackageVersion), targetFramework: _testTargetframework, source: source); @@ -251,7 +252,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests TransactionScopeOption.Required, TimeSpan.Zero)) { - installer.InstallPackage("non.existent.package.id"); + installer.InstallPackage(new PackageId("non.existent.package.id")); t.Complete(); } @@ -282,7 +283,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests { installer.InstallPackage( packageId: TestPackageId, - packageVersion: TestPackageVersion, + versionRange: VersionRange.Parse(TestPackageVersion), targetFramework: _testTargetframework, source: source); @@ -314,7 +315,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests { Action first = () => installer.InstallPackage( packageId: TestPackageId, - packageVersion: TestPackageVersion, + versionRange: VersionRange.Parse(TestPackageVersion), targetFramework: _testTargetframework, source: source); @@ -322,7 +323,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests installer.InstallPackage( packageId: TestPackageId, - packageVersion: TestPackageVersion, + versionRange: VersionRange.Parse(TestPackageVersion), targetFramework: _testTargetframework, source: source); @@ -353,7 +354,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests var package = installer.InstallPackage( packageId: TestPackageId, - packageVersion: TestPackageVersion, + versionRange: VersionRange.Parse(TestPackageVersion), targetFramework: _testTargetframework, source: source); @@ -361,7 +362,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests Action secondCall = () => installer.InstallPackage( packageId: TestPackageId, - packageVersion: TestPackageVersion, + versionRange: VersionRange.Parse(TestPackageVersion), targetFramework: _testTargetframework, source: source); @@ -376,7 +377,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests fileSystem .Directory - .Exists(store.Root.WithSubDirectories(TestPackageId).Value) + .Exists(store.Root.WithSubDirectories(TestPackageId.ToString()).Value) .Should() .BeTrue(); @@ -384,7 +385,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests fileSystem .Directory - .EnumerateFileSystemEntries(store.Root.WithSubDirectories(".stage").Value) + .EnumerateFileSystemEntries(store.Root.WithSubDirectories(ToolPackageStore.StagingDirectory).Value) .Should() .BeEmpty(); } @@ -402,7 +403,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests var package = installer.InstallPackage( packageId: TestPackageId, - packageVersion: TestPackageVersion, + versionRange: VersionRange.Parse(TestPackageVersion), targetFramework: _testTargetframework, source: source); @@ -410,7 +411,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests package.Uninstall(); - store.GetInstalledPackages(TestPackageId).Should().BeEmpty(); + store.EnumeratePackages().Should().BeEmpty(); } [Theory] @@ -426,7 +427,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests var package = installer.InstallPackage( packageId: TestPackageId, - packageVersion: TestPackageVersion, + versionRange: VersionRange.Parse(TestPackageVersion), targetFramework: _testTargetframework, source: source); @@ -438,10 +439,10 @@ namespace Microsoft.DotNet.ToolPackage.Tests { package.Uninstall(); - store.GetInstalledPackages(TestPackageId).Should().BeEmpty(); + store.EnumeratePackages().Should().BeEmpty(); } - package = store.GetInstalledPackages(TestPackageId).First(); + package = store.EnumeratePackageVersions(TestPackageId).First(); AssertPackageInstall(reporter, fileSystem, package, store); } @@ -459,7 +460,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests var package = installer.InstallPackage( packageId: TestPackageId, - packageVersion: TestPackageVersion, + versionRange: VersionRange.Parse(TestPackageVersion), targetFramework: _testTargetframework, source: source); @@ -473,7 +474,28 @@ namespace Microsoft.DotNet.ToolPackage.Tests scope.Complete(); } - store.GetInstalledPackages(TestPackageId).Should().BeEmpty(); + store.EnumeratePackages().Should().BeEmpty(); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void GivenAPackageNameWithDifferentCaseItCanInstallThePackage(bool testMockBehaviorIsInSync) + { + var nugetConfigPath = WriteNugetConfigFileToPointToTheFeed(); + + var (store, installer, reporter, fileSystem) = Setup( + useMock: testMockBehaviorIsInSync, + feeds: GetMockFeedsForConfigFile(nugetConfigPath)); + + var package = installer.InstallPackage( + packageId: new PackageId("GlObAl.TooL.coNsoLe.DemO"), + targetFramework: _testTargetframework, + nugetConfig: nugetConfigPath); + + AssertPackageInstall(reporter, fileSystem, package, store); + + package.Uninstall(); } private static void AssertPackageInstall( @@ -484,11 +506,14 @@ namespace Microsoft.DotNet.ToolPackage.Tests { reporter.Lines.Should().BeEmpty(); - package.PackageId.Should().Be(TestPackageId); - package.PackageVersion.Should().Be(TestPackageVersion); + package.Id.Should().Be(TestPackageId); + package.Version.ToNormalizedString().Should().Be(TestPackageVersion); package.PackageDirectory.Value.Should().Contain(store.Root.Value); - store.GetInstalledPackages(TestPackageId).Select(p => p.PackageVersion).Should().Equal(TestPackageVersion); + store.EnumeratePackageVersions(TestPackageId) + .Select(p => p.Version.ToNormalizedString()) + .Should() + .Equal(TestPackageVersion); package.Commands.Count.Should().Be(1); fileSystem.File.Exists(package.Commands[0].Executable.Value).Should().BeTrue($"{package.Commands[0].Executable.Value} should exist"); @@ -506,11 +531,11 @@ namespace Microsoft.DotNet.ToolPackage.Tests .Directory .EnumerateFileSystemEntries(store.Root.Value) .Should() - .NotContain(e => Path.GetFileName(e) != ".stage"); + .NotContain(e => Path.GetFileName(e) != ToolPackageStore.StagingDirectory); fileSystem .Directory - .EnumerateFileSystemEntries(store.Root.WithSubDirectories(".stage").Value) + .EnumerateFileSystemEntries(store.Root.WithSubDirectories(ToolPackageStore.StagingDirectory).Value) .Should() .BeEmpty(); } @@ -536,7 +561,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests { new MockFeedPackage { - PackageId = TestPackageId, + PackageId = TestPackageId.ToString(), Version = TestPackageVersion } } @@ -556,7 +581,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests { new MockFeedPackage { - PackageId = TestPackageId, + PackageId = TestPackageId.ToString(), Version = TestPackageVersion } } @@ -576,7 +601,7 @@ namespace Microsoft.DotNet.ToolPackage.Tests { new MockFeedPackage { - PackageId = TestPackageId, + PackageId = TestPackageId.ToString(), Version = TestPackageVersion } } @@ -642,6 +667,6 @@ namespace Microsoft.DotNet.ToolPackage.Tests private static string GetTestLocalFeedPath() => Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "TestAssetLocalNugetFeed"); private readonly string _testTargetframework = BundledTargetFramework.GetTargetFrameworkMoniker(); private const string TestPackageVersion = "1.0.4"; - private const string TestPackageId = "global.tool.console.demo"; + private static readonly PackageId TestPackageId = new PackageId("global.tool.console.demo"); } } diff --git a/test/Microsoft.DotNet.Tools.Tests.ComponentMocks/ProjectRestorerMock.cs b/test/Microsoft.DotNet.Tools.Tests.ComponentMocks/ProjectRestorerMock.cs index 2bdb35820..f4f280df5 100644 --- a/test/Microsoft.DotNet.Tools.Tests.ComponentMocks/ProjectRestorerMock.cs +++ b/test/Microsoft.DotNet.Tools.Tests.ComponentMocks/ProjectRestorerMock.cs @@ -11,6 +11,7 @@ using Microsoft.DotNet.ToolPackage; using Microsoft.DotNet.Tools; using Microsoft.DotNet.Tools.Install.Tool; using Microsoft.Extensions.EnvironmentAbstractions; +using NuGet.Versioning; namespace Microsoft.DotNet.Tools.Tests.ComponentMocks { @@ -63,7 +64,7 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks string verbosity = null) { string packageId; - string packageVersion; + VersionRange versionRange; string targetFramework; try @@ -77,7 +78,7 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks } packageId = tokens[0]; - packageVersion = tokens[1]; + versionRange = VersionRange.Parse(tokens[1]); targetFramework = tokens[2]; } catch (IOException) @@ -92,16 +93,16 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks var feedPackage = GetPackage( packageId, - packageVersion, + versionRange, nugetConfig, source); - packageVersion = feedPackage.Version; + var packageVersion = feedPackage.Version; targetFramework = string.IsNullOrEmpty(targetFramework) ? "targetFramework" : targetFramework; var fakeExecutableSubDirectory = Path.Combine( - packageId, - packageVersion, + packageId.ToLowerInvariant(), + packageVersion.ToLowerInvariant(), "tools", targetFramework, "any"); @@ -116,7 +117,7 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks private MockFeedPackage GetPackage( string packageId, - string packageVersion = null, + VersionRange versionRange = null, FilePath? nugetConfig = null, string source = null) { @@ -133,7 +134,7 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks return true; }) .SelectMany(f => f.Packages) - .Where(p => MatchPackageVersion(p, packageId, packageVersion)).OrderByDescending(p => p.Version) + .Where(p => MatchPackage(p, packageId, versionRange)).OrderByDescending(p => p.Version) .FirstOrDefault(); if (package == null) @@ -148,13 +149,15 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks return package; } - private static bool MatchPackageVersion(MockFeedPackage p, string packageId, string packageVersion) + private static bool MatchPackage(MockFeedPackage p, string packageId, VersionRange versionRange) { - if (string.IsNullOrEmpty(packageVersion)) + if (string.Compare(p.PackageId, packageId, StringComparison.CurrentCultureIgnoreCase) != 0) { - return p.PackageId == packageId; + return false; } - return p.PackageId == packageId && p.Version == packageVersion; + + return versionRange == null || + versionRange.FindBestMatch(new[] { NuGetVersion.Parse(p.Version) }) != null; } } } diff --git a/test/Microsoft.DotNet.Tools.Tests.ComponentMocks/ToolPackageInstallerMock.cs b/test/Microsoft.DotNet.Tools.Tests.ComponentMocks/ToolPackageInstallerMock.cs index cce1000f5..9966ddb6a 100644 --- a/test/Microsoft.DotNet.Tools.Tests.ComponentMocks/ToolPackageInstallerMock.cs +++ b/test/Microsoft.DotNet.Tools.Tests.ComponentMocks/ToolPackageInstallerMock.cs @@ -10,6 +10,7 @@ using Microsoft.DotNet.Cli; using Microsoft.DotNet.ToolPackage; using Microsoft.DotNet.Tools; using Microsoft.Extensions.EnvironmentAbstractions; +using NuGet.Versioning; namespace Microsoft.DotNet.Tools.Tests.ComponentMocks { @@ -35,19 +36,19 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks } public IToolPackage InstallPackage( - string packageId, - string packageVersion = null, + PackageId packageId, + VersionRange versionRange = null, string targetFramework = null, FilePath? nugetConfig = null, string source = null, string verbosity = null) { - var packageRootDirectory = _store.Root.WithSubDirectories(packageId); + var packageRootDirectory = _store.GetRootPackageDirectory(packageId); string rollbackDirectory = null; return TransactionalAction.Run( action: () => { - var stageDirectory = _store.Root.WithSubDirectories(".stage", Path.GetRandomFileName()); + var stageDirectory = _store.GetRandomStagingDirectory(); _fileSystem.Directory.CreateDirectory(stageDirectory.Value); rollbackDirectory = stageDirectory.Value; @@ -56,7 +57,7 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks // Write a fake project with the requested package id, version, and framework _fileSystem.File.WriteAllText( tempProject.Value, - $"{packageId}:{packageVersion}:{targetFramework}"); + $"{packageId}:{versionRange?.ToString("S", new VersionRangeFormatter()) ?? "*"}:{targetFramework}"); // Perform a restore on the fake project _projectRestorer.Restore( @@ -71,29 +72,22 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks _installCallback(); } - packageVersion = Path.GetFileName( - _fileSystem.Directory.EnumerateFileSystemEntries( - stageDirectory.WithSubDirectories(packageId).Value).Single()); - - var packageDirectory = packageRootDirectory.WithSubDirectories(packageVersion); + var version = _store.GetStagedPackageVersion(stageDirectory, packageId); + var packageDirectory = _store.GetPackageDirectory(packageId, version); if (_fileSystem.Directory.Exists(packageDirectory.Value)) { throw new ToolPackageException( string.Format( CommonLocalizableStrings.ToolPackageConflictPackageId, packageId, - packageVersion)); + version.ToNormalizedString())); } _fileSystem.Directory.CreateDirectory(packageRootDirectory.Value); _fileSystem.Directory.Move(stageDirectory.Value, packageDirectory.Value); rollbackDirectory = packageDirectory.Value; - return new ToolPackageMock( - _fileSystem, - packageId, - packageVersion, - packageDirectory); + return new ToolPackageMock(_fileSystem, packageId, version, packageDirectory); }, rollback: () => { if (rollbackDirectory != null && _fileSystem.Directory.Exists(rollbackDirectory)) diff --git a/test/Microsoft.DotNet.Tools.Tests.ComponentMocks/ToolPackageMock.cs b/test/Microsoft.DotNet.Tools.Tests.ComponentMocks/ToolPackageMock.cs index 5e968a288..6ad826441 100644 --- a/test/Microsoft.DotNet.Tools.Tests.ComponentMocks/ToolPackageMock.cs +++ b/test/Microsoft.DotNet.Tools.Tests.ComponentMocks/ToolPackageMock.cs @@ -8,6 +8,7 @@ using System.Linq; using Microsoft.DotNet.Cli; using Microsoft.DotNet.ToolPackage; using Microsoft.Extensions.EnvironmentAbstractions; +using NuGet.Versioning; namespace Microsoft.DotNet.Tools.Tests.ComponentMocks { @@ -19,22 +20,22 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks public ToolPackageMock( IFileSystem fileSystem, - string packageId, - string packageVersion, + PackageId id, + NuGetVersion version, DirectoryPath packageDirectory, Action uninstallCallback = null) { _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); - PackageId = packageId ?? throw new ArgumentNullException(nameof(packageId)); - PackageVersion = packageVersion ?? throw new ArgumentNullException(nameof(packageVersion)); + Id = id; + Version = version ?? throw new ArgumentNullException(nameof(version)); PackageDirectory = packageDirectory; _commands = new Lazy>(GetCommands); _uninstallCallback = uninstallCallback; } - public string PackageId { get; private set; } + public PackageId Id { get; private set; } - public string PackageVersion { get; private set; } + public NuGetVersion Version { get; private set; } public DirectoryPath PackageDirectory { get; private set; } @@ -78,7 +79,7 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks throw new ToolPackageException( string.Format( CommonLocalizableStrings.FailedToUninstallToolPackage, - PackageId, + Id, ex.Message), ex); } @@ -118,7 +119,7 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks throw new ToolPackageException( string.Format( CommonLocalizableStrings.FailedToRetrieveToolConfiguration, - PackageId, + Id, ex.Message), ex); } diff --git a/test/Microsoft.DotNet.Tools.Tests.ComponentMocks/ToolPackageStoreMock.cs b/test/Microsoft.DotNet.Tools.Tests.ComponentMocks/ToolPackageStoreMock.cs index 4681d3123..e8219560c 100644 --- a/test/Microsoft.DotNet.Tools.Tests.ComponentMocks/ToolPackageStoreMock.cs +++ b/test/Microsoft.DotNet.Tools.Tests.ComponentMocks/ToolPackageStoreMock.cs @@ -8,6 +8,7 @@ using System.Linq; using Microsoft.DotNet.Cli; using Microsoft.DotNet.ToolPackage; using Microsoft.Extensions.EnvironmentAbstractions; +using NuGet.Versioning; namespace Microsoft.DotNet.Tools.Tests.ComponentMocks { @@ -28,9 +29,66 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks public DirectoryPath Root { get; private set; } - public IEnumerable GetInstalledPackages(string packageId) + public DirectoryPath GetRandomStagingDirectory() { - var packageRootDirectory = Root.WithSubDirectories(packageId); + return Root.WithSubDirectories(ToolPackageStore.StagingDirectory, Path.GetRandomFileName()); + } + + public NuGetVersion GetStagedPackageVersion(DirectoryPath stagingDirectory, PackageId packageId) + { + if (NuGetVersion.TryParse( + Path.GetFileName( + _fileSystem.Directory.EnumerateFileSystemEntries( + stagingDirectory.WithSubDirectories(packageId.ToString()).Value).FirstOrDefault()), + out var version)) + { + return version; + } + + throw new ToolPackageException( + string.Format( + CommonLocalizableStrings.FailedToFindStagedToolPackage, + packageId)); + } + + public DirectoryPath GetRootPackageDirectory(PackageId packageId) + { + return Root.WithSubDirectories(packageId.ToString()); + } + + public DirectoryPath GetPackageDirectory(PackageId packageId, NuGetVersion version) + { + return GetRootPackageDirectory(packageId) + .WithSubDirectories(version.ToNormalizedString().ToLowerInvariant()); + } + + public IEnumerable EnumeratePackages() + { + if (!_fileSystem.Directory.Exists(Root.Value)) + { + yield break; + } + + foreach (var subdirectory in _fileSystem.Directory.EnumerateFileSystemEntries(Root.Value)) + { + var name = Path.GetFileName(subdirectory); + var packageId = new PackageId(name); + + if (name == ToolPackageStore.StagingDirectory || name != packageId.ToString()) + { + continue; + } + + foreach (var package in EnumeratePackageVersions(packageId)) + { + yield return package; + } + } + } + + public IEnumerable EnumeratePackageVersions(PackageId packageId) + { + var packageRootDirectory = Root.WithSubDirectories(packageId.ToString()); if (!_fileSystem.Directory.Exists(packageRootDirectory.Value)) { yield break; @@ -38,14 +96,23 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks foreach (var subdirectory in _fileSystem.Directory.EnumerateFileSystemEntries(packageRootDirectory.Value)) { - var version = Path.GetFileName(subdirectory); yield return new ToolPackageMock( _fileSystem, packageId, - version, - packageRootDirectory.WithSubDirectories(version), + NuGetVersion.Parse(Path.GetFileName(subdirectory)), + new DirectoryPath(subdirectory), _uninstallCallback); } } + + public IToolPackage GetPackage(PackageId packageId, NuGetVersion version) + { + var directory = GetPackageDirectory(packageId, version); + if (!_fileSystem.Directory.Exists(directory.Value)) + { + return null; + } + return new ToolPackageMock(_fileSystem, packageId, version, directory, _uninstallCallback); + } } } diff --git a/test/dotnet.Tests/CommandTests/InstallToolCommandTests.cs b/test/dotnet.Tests/CommandTests/InstallToolCommandTests.cs index 1a1f140b0..15b626428 100644 --- a/test/dotnet.Tests/CommandTests/InstallToolCommandTests.cs +++ b/test/dotnet.Tests/CommandTests/InstallToolCommandTests.cs @@ -148,17 +148,12 @@ namespace Microsoft.DotNet.Tests.Commands installToolCommand.Execute().Should().Be(1); - _reporter.Lines.Count.Should().Be(2); - _reporter - .Lines[0] + .Lines .Should() - .Contain("Simulated error"); - - _reporter - .Lines[1] - .Should() - .Contain(string.Format(LocalizableStrings.ToolInstallationFailed, PackageId)); + .Equal( + "Simulated error".Red(), + string.Format(LocalizableStrings.ToolInstallationFailed, PackageId).Red()); _fileSystem.Directory.Exists(Path.Combine(PathToPlacePackages, PackageId)).Should().BeFalse(); } @@ -205,20 +200,14 @@ namespace Microsoft.DotNet.Tests.Commands installToolCommand.Execute().Should().Be(1); - _reporter.Lines.Count.Should().Be(2); - _reporter - .Lines[0] + .Lines .Should() - .Contain( + .Equal( string.Format( LocalizableStrings.InvalidToolConfiguration, - "Simulated error")); - - _reporter - .Lines[1] - .Should() - .Contain(string.Format(LocalizableStrings.ToolInstallationFailedContactAuthor, PackageId)); + "Simulated error").Red(), + string.Format(LocalizableStrings.ToolInstallationFailedContactAuthor, PackageId).Red()); } [Fact] @@ -237,13 +226,144 @@ namespace Microsoft.DotNet.Tests.Commands _reporter .Lines - .Single() .Should() - .Contain(string.Format( + .Equal(string.Format( LocalizableStrings.InstallationSucceeded, ProjectRestorerMock.FakeCommandName, PackageId, - PackageVersion)); + PackageVersion).Green()); + } + + [Fact] + public void WhenRunWithInvalidVersionItShouldThrow() + { + const string invalidVersion = "!NotValidVersion!"; + ParseResult result = Parser.Instance.Parse($"dotnet install tool -g {PackageId} --version {invalidVersion}"); + AppliedOption appliedCommand = result["dotnet"]["install"]["tool"]; + + var installToolCommand = new InstallToolCommand( + appliedCommand, + result, + _toolPackageStore, + CreateToolPackageInstaller(), + _shellShimRepositoryMock, + new EnvironmentPathInstructionMock(_reporter, PathToPlaceShim, true), + _reporter); + + Action action = () => installToolCommand.Execute(); + + action + .ShouldThrow() + .WithMessage(string.Format( + LocalizableStrings.InvalidNuGetVersionRange, + invalidVersion)); + } + + [Fact] + public void WhenRunWithExactVersionItShouldSucceed() + { + ParseResult result = Parser.Instance.Parse($"dotnet install tool -g {PackageId} --version {PackageVersion}"); + AppliedOption appliedCommand = result["dotnet"]["install"]["tool"]; + + var installToolCommand = new InstallToolCommand( + appliedCommand, + result, + _toolPackageStore, + CreateToolPackageInstaller(), + _shellShimRepositoryMock, + new EnvironmentPathInstructionMock(_reporter, PathToPlaceShim, true), + _reporter); + + installToolCommand.Execute().Should().Be(0); + + _reporter + .Lines + .Should() + .Equal(string.Format( + LocalizableStrings.InstallationSucceeded, + ProjectRestorerMock.FakeCommandName, + PackageId, + PackageVersion).Green()); + } + + [Fact] + public void WhenRunWithValidVersionRangeItShouldSucceed() + { + ParseResult result = Parser.Instance.Parse($"dotnet install tool -g {PackageId} --version [1.0,2.0]"); + AppliedOption appliedCommand = result["dotnet"]["install"]["tool"]; + + var installToolCommand = new InstallToolCommand( + appliedCommand, + result, + _toolPackageStore, + CreateToolPackageInstaller(), + _shellShimRepositoryMock, + new EnvironmentPathInstructionMock(_reporter, PathToPlaceShim, true), + _reporter); + + installToolCommand.Execute().Should().Be(0); + + _reporter + .Lines + .Should() + .Equal(string.Format( + LocalizableStrings.InstallationSucceeded, + ProjectRestorerMock.FakeCommandName, + PackageId, + PackageVersion).Green()); + } + + [Fact] + public void WhenRunWithoutAMatchingRangeItShouldFail() + { + ParseResult result = Parser.Instance.Parse($"dotnet install tool -g {PackageId} --version [5.0,10.0]"); + AppliedOption appliedCommand = result["dotnet"]["install"]["tool"]; + + var installToolCommand = new InstallToolCommand( + appliedCommand, + result, + _toolPackageStore, + CreateToolPackageInstaller(), + _shellShimRepositoryMock, + new EnvironmentPathInstructionMock(_reporter, PathToPlaceShim, true), + _reporter); + + installToolCommand.Execute().Should().Be(1); + + _reporter + .Lines + .Should() + .Equal( + $"Error: failed to restore package {PackageId}.", // From mock implementation, not localized + LocalizableStrings.ToolInstallationRestoreFailed.Red(), + string.Format(LocalizableStrings.ToolInstallationFailed, PackageId).Red()); + } + + [Fact] + public void WhenRunWithValidVersionWildcardItShouldSucceed() + { + ParseResult result = Parser.Instance.Parse($"dotnet install tool -g {PackageId} --version 1.0.*"); + AppliedOption appliedCommand = result["dotnet"]["install"]["tool"]; + + var installToolCommand = new InstallToolCommand( + appliedCommand, + result, + _toolPackageStore, + CreateToolPackageInstaller(), + _shellShimRepositoryMock, + new EnvironmentPathInstructionMock(_reporter, PathToPlaceShim, true), + _reporter); + + installToolCommand.Execute().Should().Be(0); + + _reporter + .Lines + .Should() + .Equal(string.Format( + LocalizableStrings.InstallationSucceeded, + ProjectRestorerMock.FakeCommandName, + PackageId, + PackageVersion).Green()); } private IToolPackageInstaller CreateToolPackageInstaller(