consume bring your own shim(byos) (#9018)

If there are shims packaged by convention in nupkg. Shim Repository will simply copy it to the right location.

The query interface ToolPackageInstance will be in charge of finding the shim folder and filter the right RID. Shim Repository will pick the right file after the folder is located since Shim Repository knows the shim name and it also book keep the files at uninstallation.
During development, due to the wrong adapter level. The mock duplicated too much logic. So, I corrected the abstraction level to lower (only create shim). And replaced the existing mock with a much smaller one without any atomic control and file move, copy logic. At the same time. The chmod, which is a IO action, causes problem during tests. So I added adapter layer to it and put it in Util.
This commit is contained in:
William Li 2018-04-10 15:42:50 -07:00 committed by GitHub
parent 98a1ee6a67
commit b0ee5db411
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
47 changed files with 898 additions and 208 deletions

View file

@ -0,0 +1,29 @@
// 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.Runtime.InteropServices;
namespace Microsoft.DotNet.Cli.Utils
{
internal class FilePermissionSetter : IFilePermissionSetter
{
public void SetUserExecutionPermission(string path)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return;
}
CommandResult result = new CommandFactory()
.Create("chmod", new[] { "u+x", path })
.CaptureStdOut()
.CaptureStdErr()
.Execute();
if (result.ExitCode != 0)
{
throw new FilePermissionSettingException(result.StdErr);
}
}
}
}

View file

@ -0,0 +1,28 @@
// 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 System.Runtime.Serialization;
namespace Microsoft.DotNet.Cli.Utils
{
[Serializable]
internal class FilePermissionSettingException : Exception
{
public FilePermissionSettingException()
{
}
public FilePermissionSettingException(string message) : base(message)
{
}
public FilePermissionSettingException(string message, Exception innerException) : base(message, innerException)
{
}
protected FilePermissionSettingException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}

View file

@ -0,0 +1,7 @@
namespace Microsoft.DotNet.Cli.Utils
{
internal interface IFilePermissionSetter
{
void SetUserExecutionPermission(string path);
}
}

View file

@ -51,6 +51,11 @@ namespace Microsoft.Extensions.EnvironmentAbstractions
File.Move(source, destination); File.Move(source, destination);
} }
public void Copy(string sourceFileName, string destFileName)
{
File.Copy(sourceFileName, destFileName);
}
public void Delete(string path) public void Delete(string path)
{ {
File.Delete(path); File.Delete(path);

View file

@ -27,6 +27,8 @@ namespace Microsoft.Extensions.EnvironmentAbstractions
void Move(string source, string destination); void Move(string source, string destination);
void Copy(string source, string destination);
void Delete(string path); void Delete(string path);
} }
} }

View file

@ -640,4 +640,10 @@ setx PATH "%PATH%;{0}"
<data name="FormatVersionIsMissing" xml:space="preserve"> <data name="FormatVersionIsMissing" xml:space="preserve">
<value>Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.</value> <value>Format version is missing. This tool may not be supported in this SDK version. Please contact the author of the tool.</value>
</data> </data>
<data name="MoreThanOnePackagedShimAvailable" xml:space="preserve">
<value>More than one packaged shim is available: {0}.</value>
</data>
<data name="FailedToReadNuGetLockFile" xml:space="preserve">
<value>Failed to read NuGet LockFile for tool package '{0}': {1}</value>
</data>
</root> </root>

View file

@ -0,0 +1,53 @@
// 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.IO;
using System.Runtime.InteropServices;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.PlatformAbstractions;
using Microsoft.DotNet.Tools.Common;
using Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.ShellShim
{
internal class AppHostShellShimMaker : IAppHostShellShimMaker
{
private const string ApphostNameWithoutExtension = "apphost";
private readonly string _appHostSourceDirectory;
private readonly IFilePermissionSetter _filePermissionSetter;
public AppHostShellShimMaker(string appHostSourceDirectory = null, IFilePermissionSetter filePermissionSetter = null)
{
_appHostSourceDirectory =
appHostSourceDirectory
?? Path.Combine(ApplicationEnvironment.ApplicationBasePath, "AppHostTemplate");
_filePermissionSetter =
filePermissionSetter
?? new FilePermissionSetter();
}
public void CreateApphostShellShim(FilePath entryPoint, FilePath shimPath)
{
string appHostSourcePath;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension + ".exe");
}
else
{
appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension);
}
var appHostDestinationFilePath = shimPath.Value;
var appBinaryFilePath = PathUtility.GetRelativePath(appHostDestinationFilePath, entryPoint.Value);
EmbedAppNameInHost.EmbedAndReturnModifiedAppHostPath(
appHostSourceFilePath: appHostSourcePath,
appHostDestinationFilePath: appHostDestinationFilePath,
appBinaryFilePath: appBinaryFilePath);
_filePermissionSetter.SetUserExecutionPermission(appHostDestinationFilePath);
}
}
}

View file

@ -0,0 +1,9 @@
using Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.ShellShim
{
internal interface IAppHostShellShimMaker
{
void CreateApphostShellShim(FilePath entryPoint, FilePath shimPath);
}
}

View file

@ -1,12 +1,13 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved. // 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. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using Microsoft.Extensions.EnvironmentAbstractions; using Microsoft.Extensions.EnvironmentAbstractions;
namespace Microsoft.DotNet.ShellShim namespace Microsoft.DotNet.ShellShim
{ {
internal interface IShellShimRepository internal interface IShellShimRepository
{ {
void CreateShim(FilePath targetExecutablePath, string commandName); void CreateShim(FilePath targetExecutablePath, string commandName, IReadOnlyList<FilePath> packagedShims = null);
void RemoveShim(string commandName); void RemoveShim(string commandName);
} }

View file

@ -21,16 +21,24 @@ namespace Microsoft.DotNet.ShellShim
private const string ApphostNameWithoutExtension = "apphost"; private const string ApphostNameWithoutExtension = "apphost";
private readonly DirectoryPath _shimsDirectory; private readonly DirectoryPath _shimsDirectory;
private readonly string _appHostSourceDirectory; private readonly IFileSystem _fileSystem;
private readonly IAppHostShellShimMaker _appHostShellShimMaker;
private readonly IFilePermissionSetter _filePermissionSetter;
public ShellShimRepository(DirectoryPath shimsDirectory, string appHostSourcePath = null) public ShellShimRepository(
DirectoryPath shimsDirectory,
string appHostSourceDirectory = null,
IFileSystem fileSystem = null,
IAppHostShellShimMaker appHostShellShimMaker = null,
IFilePermissionSetter filePermissionSetter = null)
{ {
_shimsDirectory = shimsDirectory; _shimsDirectory = shimsDirectory;
_appHostSourceDirectory = appHostSourcePath ?? Path.Combine(ApplicationEnvironment.ApplicationBasePath, _fileSystem = fileSystem ?? new FileSystemWrapper();
"AppHostTemplate"); _appHostShellShimMaker = appHostShellShimMaker ?? new AppHostShellShimMaker(appHostSourceDirectory: appHostSourceDirectory);
_filePermissionSetter = filePermissionSetter ?? new FilePermissionSetter();
} }
public void CreateShim(FilePath targetExecutablePath, string commandName) public void CreateShim(FilePath targetExecutablePath, string commandName, IReadOnlyList<FilePath> packagedShims = null)
{ {
if (string.IsNullOrEmpty(targetExecutablePath.Value)) if (string.IsNullOrEmpty(targetExecutablePath.Value))
{ {
@ -54,19 +62,27 @@ namespace Microsoft.DotNet.ShellShim
{ {
try try
{ {
if (!Directory.Exists(_shimsDirectory.Value)) if (!_fileSystem.Directory.Exists(_shimsDirectory.Value))
{ {
Directory.CreateDirectory(_shimsDirectory.Value); _fileSystem.Directory.CreateDirectory(_shimsDirectory.Value);
} }
CreateApphostShim( if (TryGetPackagedShim(packagedShims, commandName, out FilePath? packagedShim))
commandName,
entryPoint: targetExecutablePath);
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
SetUserExecutionPermission(GetShimPath(commandName)); _fileSystem.File.Copy(packagedShim.Value.Value, GetShimPath(commandName).Value);
_filePermissionSetter.SetUserExecutionPermission(GetShimPath(commandName).Value);
} }
else
{
_appHostShellShimMaker.CreateApphostShellShim(
targetExecutablePath,
GetShimPath(commandName));
}
}
catch (FilePermissionSettingException ex)
{
throw new ShellShimException(
string.Format(CommonLocalizableStrings.FailedSettingShimPermissions, ex.Message));
} }
catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException) catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException)
{ {
@ -80,7 +96,7 @@ namespace Microsoft.DotNet.ShellShim
} }
}, },
rollback: () => { rollback: () => {
foreach (var file in GetShimFiles(commandName).Where(f => File.Exists(f.Value))) foreach (var file in GetShimFiles(commandName).Where(f => _fileSystem.File.Exists(f.Value)))
{ {
File.Delete(file.Value); File.Delete(file.Value);
} }
@ -94,10 +110,10 @@ namespace Microsoft.DotNet.ShellShim
action: () => { action: () => {
try try
{ {
foreach (var file in GetShimFiles(commandName).Where(f => File.Exists(f.Value))) foreach (var file in GetShimFiles(commandName).Where(f => _fileSystem.File.Exists(f.Value)))
{ {
var tempPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); var tempPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
File.Move(file.Value, tempPath); _fileSystem.File.Move(file.Value, tempPath);
files[file.Value] = tempPath; files[file.Value] = tempPath;
} }
} }
@ -115,38 +131,17 @@ namespace Microsoft.DotNet.ShellShim
commit: () => { commit: () => {
foreach (var value in files.Values) foreach (var value in files.Values)
{ {
File.Delete(value); _fileSystem.File.Delete(value);
} }
}, },
rollback: () => { rollback: () => {
foreach (var kvp in files) foreach (var kvp in files)
{ {
File.Move(kvp.Value, kvp.Key); _fileSystem.File.Move(kvp.Value, kvp.Key);
} }
}); });
} }
private void CreateApphostShim(string commandName, FilePath entryPoint)
{
string appHostSourcePath;
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension + ".exe");
}
else
{
appHostSourcePath = Path.Combine(_appHostSourceDirectory, ApphostNameWithoutExtension);
}
var appHostDestinationFilePath = GetShimPath(commandName).Value;
var appBinaryFilePath = PathUtility.GetRelativePath(appHostDestinationFilePath, entryPoint.Value);
EmbedAppNameInHost.EmbedAndReturnModifiedAppHostPath(
appHostSourceFilePath: appHostSourcePath,
appHostDestinationFilePath: appHostDestinationFilePath,
appBinaryFilePath: appBinaryFilePath);
}
private class StartupOptions private class StartupOptions
{ {
public string appRoot { get; set; } public string appRoot { get; set; }
@ -159,7 +154,7 @@ namespace Microsoft.DotNet.ShellShim
private bool ShimExists(string commandName) private bool ShimExists(string commandName)
{ {
return GetShimFiles(commandName).Any(p => File.Exists(p.Value)); return GetShimFiles(commandName).Any(p => _fileSystem.File.Exists(p.Value));
} }
private IEnumerable<FilePath> GetShimFiles(string commandName) private IEnumerable<FilePath> GetShimFiles(string commandName)
@ -184,24 +179,37 @@ namespace Microsoft.DotNet.ShellShim
} }
} }
private static void SetUserExecutionPermission(FilePath path) private bool TryGetPackagedShim(
IReadOnlyList<FilePath> packagedShims,
string commandName,
out FilePath? packagedShim)
{ {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) packagedShim = null;
if (packagedShims != null && packagedShims.Count > 0)
{ {
return; FilePath[] candidatepackagedShim =
packagedShims
.Where(s => string.Equals(
Path.GetFileName(s.Value),
Path.GetFileName(GetShimPath(commandName).Value))).ToArray();
if (candidatepackagedShim.Length > 1)
{
throw new ShellShimException(
string.Format(
CommonLocalizableStrings.MoreThanOnePackagedShimAvailable,
string.Join(';', candidatepackagedShim)));
}
if (candidatepackagedShim.Length == 1)
{
packagedShim = candidatepackagedShim.Single();
return true;
}
} }
CommandResult result = new CommandFactory() return false;
.Create("chmod", new[] { "u+x", path.Value })
.CaptureStdOut()
.CaptureStdErr()
.Execute();
if (result.ExitCode != 0)
{
throw new ShellShimException(
string.Format(CommonLocalizableStrings.FailedSettingShimPermissions, result.StdErr));
}
} }
} }
} }

View file

@ -20,6 +20,8 @@ namespace Microsoft.DotNet.ToolPackage
IEnumerable<string> Warnings { get; } IEnumerable<string> Warnings { get; }
IReadOnlyList<FilePath> PackagedShims { get; }
void Uninstall(); void Uninstall();
} }
} }

View file

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved. // 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. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
using System.Linq; using System.Linq;
using NuGet.ProjectModel; using NuGet.ProjectModel;
@ -22,36 +23,60 @@ namespace Microsoft.DotNet.ToolPackage
string[] entryPointPathInArray = SplitPathByDirectorySeparator(targetRelativeFilePath); string[] entryPointPathInArray = SplitPathByDirectorySeparator(targetRelativeFilePath);
return entryPointPathInArray.Length >= 1 return entryPointPathInArray.Length >= 1
&& PathInLockFileDirectoriesStartWithToolsAndFollowsTwoSubFolder() && PathInLockFileDirectoriesStartWithToolsAndFollowsTwoSubFolder(
&& SubPathMatchesTargetFilePath(); pathInLockFilePathInArray,
entryPointPathInArray)
&& SubPathMatchesTargetFilePath(pathInLockFilePathInArray, entryPointPathInArray);
}
bool SubPathMatchesTargetFilePath() /// <summary>
/// Check if LockFileItem is under targetRelativePath directory.
/// The path in LockFileItem is in pattern tools/TFM/RID/my/tool.dll. Tools/TFM/RID is selected by NuGet.
/// And there will be only one TFM/RID combination.
/// When "my/folder/of/tool/tools.dll" part under targetRelativePath "my/folder/of" or "my/folder", return true.
/// </summary>
internal static bool MatchesDirectoryPath(LockFileItem lockFileItem, string targetRelativePath)
{
string[] pathInLockFilePathInArray = SplitPathByDirectorySeparator(lockFileItem.Path);
string[] targetDirectoryPathInArray = SplitPathByDirectorySeparator(targetRelativePath);
return pathInLockFilePathInArray[0] == "tools"
&& SubPathMatchesTargetFilePath(pathInLockFilePathInArray, targetDirectoryPathInArray);
}
private static bool SubPathMatchesTargetFilePath(string[] pathInLockFilePathInArray, string[] targetInArray)
{
string[] pathAfterToolsTfmRid = pathInLockFilePathInArray.Skip(3).ToArray();
return !targetInArray
.Where((directoryOnEveryLevel, i) => directoryOnEveryLevel != pathAfterToolsTfmRid[i])
.Any();
}
private static bool PathInLockFileDirectoriesStartWithToolsAndFollowsTwoSubFolder(
string[] pathInLockFilePathInArray,
string[] targetInArray)
{
if (pathInLockFilePathInArray.Length - targetInArray.Length != 3)
{ {
string[] pathAfterToolsTfmRid = pathInLockFilePathInArray.Skip(3).ToArray(); return false;
return !pathAfterToolsTfmRid
.Where((directoryOnEveryLevel, i) => directoryOnEveryLevel != entryPointPathInArray[i])
.Any();
} }
bool PathInLockFileDirectoriesStartWithToolsAndFollowsTwoSubFolder() if (pathInLockFilePathInArray[0] != "tools")
{ {
if (pathInLockFilePathInArray.Length - entryPointPathInArray.Length != 3) return false;
{
return false;
}
if (pathInLockFilePathInArray[0] != "tools")
{
return false;
}
return true;
} }
string[] SplitPathByDirectorySeparator(string path) return true;
}
private static string[] SplitPathByDirectorySeparator(string path)
{
if (string.IsNullOrEmpty(path))
{ {
return path.Split('\\', '/'); return new string[0];
} }
return path.Split('\\', '/');
} }
} }
} }

View file

@ -7,13 +7,17 @@ using Microsoft.DotNet.Tools;
using Microsoft.Extensions.EnvironmentAbstractions; using Microsoft.Extensions.EnvironmentAbstractions;
using NuGet.ProjectModel; using NuGet.ProjectModel;
using NuGet.Versioning; using NuGet.Versioning;
using Microsoft.DotNet.Cli.Utils;
namespace Microsoft.DotNet.ToolPackage namespace Microsoft.DotNet.ToolPackage
{ {
// This is named "ToolPackageInstance" because "ToolPackage" would conflict with the namespace // This is named "ToolPackageInstance" because "ToolPackage" would conflict with the namespace
internal class ToolPackageInstance : IToolPackage internal class ToolPackageInstance : IToolPackage
{ {
private const string PackagedShimsDirectoryConvention = "shims";
public IEnumerable<string> Warnings => _toolConfiguration.Value.Warnings; public IEnumerable<string> Warnings => _toolConfiguration.Value.Warnings;
public PackageId Id { get; private set; } public PackageId Id { get; private set; }
public NuGetVersion Version { get; private set; } public NuGetVersion Version { get; private set; }
@ -28,12 +32,21 @@ namespace Microsoft.DotNet.ToolPackage
} }
} }
public IReadOnlyList<FilePath> PackagedShims
{
get
{
return _packagedShims.Value;
}
}
private const string AssetsFileName = "project.assets.json"; private const string AssetsFileName = "project.assets.json";
private const string ToolSettingsFileName = "DotnetToolSettings.xml"; private const string ToolSettingsFileName = "DotnetToolSettings.xml";
private IToolPackageStore _store; private IToolPackageStore _store;
private Lazy<IReadOnlyList<CommandSettings>> _commands; private Lazy<IReadOnlyList<CommandSettings>> _commands;
private Lazy<ToolConfiguration> _toolConfiguration; private Lazy<ToolConfiguration> _toolConfiguration;
private Lazy<IReadOnlyList<FilePath>> _packagedShims;
public ToolPackageInstance( public ToolPackageInstance(
IToolPackageStore store, IToolPackageStore store,
@ -43,6 +56,7 @@ namespace Microsoft.DotNet.ToolPackage
{ {
_store = store ?? throw new ArgumentNullException(nameof(store)); _store = store ?? throw new ArgumentNullException(nameof(store));
_commands = new Lazy<IReadOnlyList<CommandSettings>>(GetCommands); _commands = new Lazy<IReadOnlyList<CommandSettings>>(GetCommands);
_packagedShims = new Lazy<IReadOnlyList<FilePath>>(GetPackagedShims);
Id = id; Id = id;
Version = version ?? throw new ArgumentNullException(nameof(version)); Version = version ?? throw new ArgumentNullException(nameof(version));
@ -107,11 +121,11 @@ namespace Microsoft.DotNet.ToolPackage
try try
{ {
var commands = new List<CommandSettings>(); var commands = new List<CommandSettings>();
var lockFile = new LockFileFormat().Read(PackageDirectory.WithFile(AssetsFileName).Value); LockFile lockFile = new LockFileFormat().Read(PackageDirectory.WithFile(AssetsFileName).Value);
var library = FindLibraryInLockFile(lockFile); LockFileTargetLibrary library = FindLibraryInLockFile(lockFile);
ToolConfiguration configuration = _toolConfiguration.Value; ToolConfiguration configuration = _toolConfiguration.Value;
var entryPointFromLockFile = FindItemInTargetLibrary(library, configuration.ToolAssemblyEntryPoint); LockFileItem entryPointFromLockFile = FindItemInTargetLibrary(library, configuration.ToolAssemblyEntryPoint);
if (entryPointFromLockFile == null) if (entryPointFromLockFile == null)
{ {
throw new ToolConfigurationException( throw new ToolConfigurationException(
@ -125,11 +139,7 @@ namespace Microsoft.DotNet.ToolPackage
commands.Add(new CommandSettings( commands.Add(new CommandSettings(
configuration.CommandName, configuration.CommandName,
"dotnet", "dotnet",
PackageDirectory LockFileRelativePathToFullFilePath(entryPointFromLockFile.Path, library)));
.WithSubDirectories(
Id.ToString(),
library.Version.ToNormalizedString())
.WithFile(entryPointFromLockFile.Path)));
return commands; return commands;
} }
@ -143,6 +153,15 @@ namespace Microsoft.DotNet.ToolPackage
} }
} }
private FilePath LockFileRelativePathToFullFilePath(string lockFileRelativePath, LockFileTargetLibrary library)
{
return PackageDirectory
.WithSubDirectories(
Id.ToString(),
library.Version.ToNormalizedString())
.WithFile(lockFileRelativePath);
}
private ToolConfiguration GetToolConfiguration() private ToolConfiguration GetToolConfiguration()
{ {
try try
@ -161,6 +180,55 @@ namespace Microsoft.DotNet.ToolPackage
} }
} }
private IReadOnlyList<FilePath> GetPackagedShims()
{
LockFileTargetLibrary library;
try
{
LockFile lockFile = new LockFileFormat().Read(PackageDirectory.WithFile(AssetsFileName).Value);
library = FindLibraryInLockFile(lockFile);
}
catch (Exception ex) when (ex is UnauthorizedAccessException || ex is IOException)
{
throw new ToolPackageException(
string.Format(
CommonLocalizableStrings.FailedToReadNuGetLockFile,
Id,
ex.Message),
ex);
}
IEnumerable<LockFileItem> filesUnderShimsDirectory = library
?.ToolsAssemblies
?.Where(t => LockFileMatcher.MatchesDirectoryPath(t, PackagedShimsDirectoryConvention));
if (filesUnderShimsDirectory == null)
{
return Array.Empty<FilePath>();
}
IEnumerable<string> allAvailableShimRuntimeIdentifiers = filesUnderShimsDirectory
.Select(f => f.Path.Split('\\', '/')?[4]) // ex: "tools/netcoreapp2.1/any/shims/osx-x64/demo" osx-x64 is at [4]
.Where(f => !string.IsNullOrEmpty(f));
if (new FrameworkDependencyFile().TryGetMostFitRuntimeIdentifier(
DotnetFiles.VersionFileObject.BuildRid,
allAvailableShimRuntimeIdentifiers.ToArray(),
out var mostFitRuntimeIdentifier))
{
return library
?.ToolsAssemblies
?.Where(l =>
LockFileMatcher.MatchesDirectoryPath(l, $"{PackagedShimsDirectoryConvention}/{mostFitRuntimeIdentifier}"))
.Select(l => LockFileRelativePathToFullFilePath(l.Path, library)).ToArray()
?? Array.Empty<FilePath>();
}
else
{
return Array.Empty<FilePath>();
}
}
private ToolConfiguration DeserializeToolConfiguration(string ToolSettingsFileName, LockFileTargetLibrary library) private ToolConfiguration DeserializeToolConfiguration(string ToolSettingsFileName, LockFileTargetLibrary library)
{ {
var dotnetToolSettings = FindItemInTargetLibrary(library, ToolSettingsFileName); var dotnetToolSettings = FindItemInTargetLibrary(library, ToolSettingsFileName);

View file

@ -142,7 +142,7 @@ namespace Microsoft.DotNet.Tools.Tool.Install
foreach (var command in package.Commands) foreach (var command in package.Commands)
{ {
shellShimRepository.CreateShim(command.Executable, command.Name); shellShimRepository.CreateShim(command.Executable, command.Name, package.PackagedShims);
} }
scope.Complete(); scope.Complete();

View file

@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target> <target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
<note /> <note />
</trans-unit> </trans-unit>
<trans-unit id="MoreThanOnePackagedShimAvailable">
<source>More than one packaged shim is available: {0}.</source>
<target state="new">More than one packaged shim is available: {0}.</target>
<note />
</trans-unit>
<trans-unit id="FailedToReadNuGetLockFile">
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
<note />
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View file

@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target> <target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
<note /> <note />
</trans-unit> </trans-unit>
<trans-unit id="MoreThanOnePackagedShimAvailable">
<source>More than one packaged shim is available: {0}.</source>
<target state="new">More than one packaged shim is available: {0}.</target>
<note />
</trans-unit>
<trans-unit id="FailedToReadNuGetLockFile">
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
<note />
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View file

@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target> <target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
<note /> <note />
</trans-unit> </trans-unit>
<trans-unit id="MoreThanOnePackagedShimAvailable">
<source>More than one packaged shim is available: {0}.</source>
<target state="new">More than one packaged shim is available: {0}.</target>
<note />
</trans-unit>
<trans-unit id="FailedToReadNuGetLockFile">
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
<note />
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View file

@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target> <target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
<note /> <note />
</trans-unit> </trans-unit>
<trans-unit id="MoreThanOnePackagedShimAvailable">
<source>More than one packaged shim is available: {0}.</source>
<target state="new">More than one packaged shim is available: {0}.</target>
<note />
</trans-unit>
<trans-unit id="FailedToReadNuGetLockFile">
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
<note />
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View file

@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target> <target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
<note /> <note />
</trans-unit> </trans-unit>
<trans-unit id="MoreThanOnePackagedShimAvailable">
<source>More than one packaged shim is available: {0}.</source>
<target state="new">More than one packaged shim is available: {0}.</target>
<note />
</trans-unit>
<trans-unit id="FailedToReadNuGetLockFile">
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
<note />
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View file

@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target> <target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
<note /> <note />
</trans-unit> </trans-unit>
<trans-unit id="MoreThanOnePackagedShimAvailable">
<source>More than one packaged shim is available: {0}.</source>
<target state="new">More than one packaged shim is available: {0}.</target>
<note />
</trans-unit>
<trans-unit id="FailedToReadNuGetLockFile">
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
<note />
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View file

@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target> <target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
<note /> <note />
</trans-unit> </trans-unit>
<trans-unit id="MoreThanOnePackagedShimAvailable">
<source>More than one packaged shim is available: {0}.</source>
<target state="new">More than one packaged shim is available: {0}.</target>
<note />
</trans-unit>
<trans-unit id="FailedToReadNuGetLockFile">
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
<note />
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View file

@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target> <target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
<note /> <note />
</trans-unit> </trans-unit>
<trans-unit id="MoreThanOnePackagedShimAvailable">
<source>More than one packaged shim is available: {0}.</source>
<target state="new">More than one packaged shim is available: {0}.</target>
<note />
</trans-unit>
<trans-unit id="FailedToReadNuGetLockFile">
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
<note />
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View file

@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target> <target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
<note /> <note />
</trans-unit> </trans-unit>
<trans-unit id="MoreThanOnePackagedShimAvailable">
<source>More than one packaged shim is available: {0}.</source>
<target state="new">More than one packaged shim is available: {0}.</target>
<note />
</trans-unit>
<trans-unit id="FailedToReadNuGetLockFile">
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
<note />
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View file

@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target> <target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
<note /> <note />
</trans-unit> </trans-unit>
<trans-unit id="MoreThanOnePackagedShimAvailable">
<source>More than one packaged shim is available: {0}.</source>
<target state="new">More than one packaged shim is available: {0}.</target>
<note />
</trans-unit>
<trans-unit id="FailedToReadNuGetLockFile">
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
<note />
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View file

@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target> <target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
<note /> <note />
</trans-unit> </trans-unit>
<trans-unit id="MoreThanOnePackagedShimAvailable">
<source>More than one packaged shim is available: {0}.</source>
<target state="new">More than one packaged shim is available: {0}.</target>
<note />
</trans-unit>
<trans-unit id="FailedToReadNuGetLockFile">
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
<note />
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View file

@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target> <target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
<note /> <note />
</trans-unit> </trans-unit>
<trans-unit id="MoreThanOnePackagedShimAvailable">
<source>More than one packaged shim is available: {0}.</source>
<target state="new">More than one packaged shim is available: {0}.</target>
<note />
</trans-unit>
<trans-unit id="FailedToReadNuGetLockFile">
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
<note />
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View file

@ -883,6 +883,16 @@ setx PATH "%PATH%;{0}"
<target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target> <target state="new">Failed to add '{0}' to the PATH environment variable. Please add this directory to your PATH to use tools installed with 'dotnet tool install'.</target>
<note /> <note />
</trans-unit> </trans-unit>
<trans-unit id="MoreThanOnePackagedShimAvailable">
<source>More than one packaged shim is available: {0}.</source>
<target state="new">More than one packaged shim is available: {0}.</target>
<note />
</trans-unit>
<trans-unit id="FailedToReadNuGetLockFile">
<source>Failed to read NuGet LockFile for tool package '{0}': {1}</source>
<target state="new">Failed to read NuGet LockFile for tool package '{0}': {1}</target>
<note />
</trans-unit>
</body> </body>
</file> </file>
</xliff> </xliff>

View file

@ -134,6 +134,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
{ {
throw new UnauthorizedAccessException(); throw new UnauthorizedAccessException();
} }
public void Copy(string source, string destination)
{
throw new UnauthorizedAccessException();
}
} }
private class NoPermissionDirectoryFake : IDirectory private class NoPermissionDirectoryFake : IDirectory

View file

@ -286,6 +286,11 @@ namespace Microsoft.DotNet.Configurer.UnitTests
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public void Copy(string source, string destination)
{
throw new NotImplementedException();
}
} }
private class MockStream : MemoryStream private class MockStream : MemoryStream

View file

@ -119,7 +119,7 @@ namespace Microsoft.DotNet.ShellShim.Tests
IShellShimRepository shellShimRepository; IShellShimRepository shellShimRepository;
if (testMockBehaviorIsInSync) if (testMockBehaviorIsInSync)
{ {
shellShimRepository = new ShellShimRepositoryMock(new DirectoryPath(pathToShim)); shellShimRepository = GetShellShimRepositoryWithMockMaker(pathToShim);
} }
else else
{ {
@ -161,7 +161,7 @@ namespace Microsoft.DotNet.ShellShim.Tests
IShellShimRepository shellShimRepository; IShellShimRepository shellShimRepository;
if (testMockBehaviorIsInSync) if (testMockBehaviorIsInSync)
{ {
shellShimRepository = new ShellShimRepositoryMock(new DirectoryPath(pathToShim)); shellShimRepository = GetShellShimRepositoryWithMockMaker(pathToShim);
} }
else else
{ {
@ -198,7 +198,7 @@ namespace Microsoft.DotNet.ShellShim.Tests
IShellShimRepository shellShimRepository; IShellShimRepository shellShimRepository;
if (testMockBehaviorIsInSync) if (testMockBehaviorIsInSync)
{ {
shellShimRepository = new ShellShimRepositoryMock(new DirectoryPath(pathToShim)); shellShimRepository = GetShellShimRepositoryWithMockMaker(pathToShim);
} }
else else
{ {
@ -223,7 +223,7 @@ namespace Microsoft.DotNet.ShellShim.Tests
IShellShimRepository shellShimRepository; IShellShimRepository shellShimRepository;
if (testMockBehaviorIsInSync) if (testMockBehaviorIsInSync)
{ {
shellShimRepository = new ShellShimRepositoryMock(new DirectoryPath(pathToShim)); shellShimRepository = GetShellShimRepositoryWithMockMaker(pathToShim);
} }
else else
{ {
@ -252,7 +252,7 @@ namespace Microsoft.DotNet.ShellShim.Tests
IShellShimRepository shellShimRepository; IShellShimRepository shellShimRepository;
if (testMockBehaviorIsInSync) if (testMockBehaviorIsInSync)
{ {
shellShimRepository = new ShellShimRepositoryMock(new DirectoryPath(pathToShim)); shellShimRepository = GetShellShimRepositoryWithMockMaker(pathToShim);
} }
else else
{ {
@ -288,7 +288,7 @@ namespace Microsoft.DotNet.ShellShim.Tests
IShellShimRepository shellShimRepository; IShellShimRepository shellShimRepository;
if (testMockBehaviorIsInSync) if (testMockBehaviorIsInSync)
{ {
shellShimRepository = new ShellShimRepositoryMock(new DirectoryPath(pathToShim)); shellShimRepository = GetShellShimRepositoryWithMockMaker(pathToShim);
} }
else else
{ {
@ -315,6 +315,67 @@ namespace Microsoft.DotNet.ShellShim.Tests
Directory.EnumerateFileSystemEntries(pathToShim).Should().BeEmpty(); Directory.EnumerateFileSystemEntries(pathToShim).Should().BeEmpty();
} }
[Fact]
public void WhenPackagedShimProvidedItCopies()
{
const string tokenToIdentifyCopiedShim = "packagedShim";
var shellCommandName = nameof(ShellShimRepositoryTests) + Path.GetRandomFileName();
var pathToShim = GetNewCleanFolderUnderTempRoot();
var packagedShimFolder = GetNewCleanFolderUnderTempRoot();
var dummyShimPath = Path.Combine(packagedShimFolder, shellCommandName);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
dummyShimPath = dummyShimPath + ".exe";
}
File.WriteAllText(dummyShimPath, tokenToIdentifyCopiedShim);
ShellShimRepository shellShimRepository = GetShellShimRepositoryWithMockMaker(pathToShim);
shellShimRepository.CreateShim(
new FilePath("dummy.dll"),
shellCommandName,
new[] {new FilePath(dummyShimPath)});
var createdShim = Directory.EnumerateFileSystemEntries(pathToShim).Single();
File.ReadAllText(createdShim).Should().Contain(tokenToIdentifyCopiedShim);
}
[Fact]
public void WhenMutipleSameNamePackagedShimProvidedItThrows()
{
const string tokenToIdentifyCopiedShim = "packagedShim";
var shellCommandName = nameof(ShellShimRepositoryTests) + Path.GetRandomFileName();
var pathToShim = GetNewCleanFolderUnderTempRoot();
var packagedShimFolder = GetNewCleanFolderUnderTempRoot();
var dummyShimPath = Path.Combine(packagedShimFolder, shellCommandName);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
dummyShimPath = dummyShimPath + ".exe";
}
File.WriteAllText(dummyShimPath, tokenToIdentifyCopiedShim);
ShellShimRepository shellShimRepository = GetShellShimRepositoryWithMockMaker(pathToShim);
FilePath[] filePaths = new[] { new FilePath(dummyShimPath), new FilePath("path" + dummyShimPath) };
Action a = () => shellShimRepository.CreateShim(
new FilePath("dummy.dll"),
shellCommandName,
new[] { new FilePath(dummyShimPath), new FilePath("path" + dummyShimPath) });
a.ShouldThrow<ShellShimException>()
.And.Message
.Should().Contain(
string.Format(
CommonLocalizableStrings.MoreThanOnePackagedShimAvailable,
string.Join(';', filePaths)));
}
private static void MakeNameConflictingCommand(string pathToPlaceShim, string shellCommandName) private static void MakeNameConflictingCommand(string pathToPlaceShim, string shellCommandName)
{ {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
@ -440,5 +501,12 @@ namespace Microsoft.DotNet.ShellShim.Tests
return CleanFolderUnderTempRoot.FullName; return CleanFolderUnderTempRoot.FullName;
} }
private ShellShimRepository GetShellShimRepositoryWithMockMaker(string pathToShim)
{
return new ShellShimRepository(
new DirectoryPath(pathToShim),
appHostShellShimMaker: new AppHostShellShimMakerMock());
}
} }
} }

View file

@ -10,7 +10,6 @@ namespace Microsoft.DotNet.ToolPackage.Tests
{ {
public class LockFileMatcherTests : TestBase public class LockFileMatcherTests : TestBase
{ {
[Theory] [Theory]
[InlineData("tools/netcoreapp1.1/any/tool.dll", "tool.dll", true)] [InlineData("tools/netcoreapp1.1/any/tool.dll", "tool.dll", true)]
[InlineData(@"tools\netcoreapp1.1\any\subDirectory\tool.dll", "subDirectory/tool.dll", true)] [InlineData(@"tools\netcoreapp1.1\any\subDirectory\tool.dll", "subDirectory/tool.dll", true)]
@ -24,5 +23,20 @@ namespace Microsoft.DotNet.ToolPackage.Tests
LockFileMatcher.MatchesFile(new LockFileItem(pathInLockFileItem), targetRelativeFilePath) LockFileMatcher.MatchesFile(new LockFileItem(pathInLockFileItem), targetRelativeFilePath)
.Should().Be(shouldMatch); .Should().Be(shouldMatch);
} }
[Theory]
[InlineData("tools/netcoreapp1.1/any/tool.dll", "", true)]
[InlineData(@"tools\netcoreapp1.1\any\subDirectory\tool.dll", "subDirectory", true)]
[InlineData(@"tools\netcoreapp1.1\any\subDirectory\tool.dll", "sub", false)]
[InlineData("tools/netcoreapp1.1/any/subDirectory/tool.dll", "any/subDirectory", false)]
public void MatchesDirectoryPathTests(
string pathInLockFileItem,
string targetRelativeFilePath,
bool shouldMatch)
{
LockFileMatcher.MatchesDirectoryPath(new LockFileItem(pathInLockFileItem), targetRelativeFilePath)
.Should().Be(shouldMatch);
}
} }
} }

View file

@ -45,6 +45,11 @@
<Content Remove="SampleGlobalTool/**" /> <Content Remove="SampleGlobalTool/**" />
<EmbeddedResource Remove="SampleGlobalTool/**" /> <EmbeddedResource Remove="SampleGlobalTool/**" />
<None Remove="SampleGlobalTool/**" /> <None Remove="SampleGlobalTool/**" />
<Compile Remove="SampleGlobalToolWithShim/**" />
<Content Remove="SampleGlobalToolWithShim/**" />
<EmbeddedResource Remove="SampleGlobalToolWithShim/**" />
<None Remove="SampleGlobalToolWithShim/**" />
</ItemGroup> </ItemGroup>
<UsingTask TaskName="DownloadFile" AssemblyFile="$(CLIBuildDll)" /> <UsingTask TaskName="DownloadFile" AssemblyFile="$(CLIBuildDll)" />
@ -59,4 +64,17 @@
<MSBuild BuildInParallel="False" Projects="SampleGlobalTool/consoledemo.csproj" Targets="pack" Properties="Configuration=Release;NuspecFile=includepublish.nuspec;NuspecBasePath=$(testAssetSourceRoot);PackageOutputPath=$(OutputPath)/TestAssetLocalNugetFeed"> <MSBuild BuildInParallel="False" Projects="SampleGlobalTool/consoledemo.csproj" Targets="pack" Properties="Configuration=Release;NuspecFile=includepublish.nuspec;NuspecBasePath=$(testAssetSourceRoot);PackageOutputPath=$(OutputPath)/TestAssetLocalNugetFeed">
</MSBuild> </MSBuild>
</Target> </Target>
<Target Name="CreateNupkgWithShimFromSource" BeforeTargets="Build">
<PropertyGroup>
<testAssetSourceRoot>$(BaseOutputPath)/TestAsset/SampleGlobalToolWithShim</testAssetSourceRoot>
</PropertyGroup>
<Copy SourceFiles="SampleGlobalToolWithShim/DotnetToolSettings.xml" DestinationFolder="$(testAssetSourceRoot)" />
<Copy SourceFiles="SampleGlobalToolWithShim/dummyshim" DestinationFolder="$(testAssetSourceRoot)" />
<Copy SourceFiles="SampleGlobalToolWithShim/dummyshim.exe" DestinationFolder="$(testAssetSourceRoot)" />
<MSBuild BuildInParallel="False" Projects="SampleGlobalToolWithShim/consoledemo.csproj" Targets="Restore;Build;Publish" Properties="Configuration=Release;BaseOutputPath=$(testAssetSourceRoot)/bin/">
</MSBuild>
<MSBuild BuildInParallel="False" Projects="SampleGlobalToolWithShim/consoledemo.csproj" Targets="pack" Properties="Configuration=Release;NuspecFile=includepublish.nuspec;NuspecBasePath=$(testAssetSourceRoot);PackageOutputPath=$(OutputPath)/TestAssetLocalNugetFeed">
</MSBuild>
</Target>
</Project> </Project>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<DotNetCliTool Version="1" >
<Commands>
<Command Name="demo" EntryPoint="consoledemo.dll" Runner="dotnet" />
</Commands>
</DotNetCliTool>

View file

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
namespace consoledemo
{
class Program
{
static void Main(string[] args)
{
var greeting = "Hello World from Global Tool";
Console.WriteLine(greeting);
}
}
}

View file

@ -0,0 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<AssemblyName>consoledemo</AssemblyName>
</PropertyGroup>
</Project>

View file

@ -0,0 +1 @@
packagedshim

View file

@ -0,0 +1 @@
packagedshim

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata>
<id>global.tool.console.demo.with.shim</id>
<version>1.0.4</version>
<description>test app</description>
<authors>testauthor</authors>
<packageTypes>
<packageType name="DotnetTool" />
</packageTypes>
</metadata>
<files>
<file src="bin\Release\netcoreapp2.1\publish\*.*" target="tools\netcoreapp2.1\any\" />
<file src="DotnetToolSettings.xml" target="tools\netcoreapp2.1\any\DotnetToolSettings.xml" />
<file src="dummyshim.exe" target="tools\netcoreapp2.1\any\shims\win-x64\demo.exe" />
<file src="dummyshim.exe" target="tools\netcoreapp2.1\any\shims\win-x86\demo.exe" />
<file src="dummyshim" target="tools\netcoreapp2.1\any\shims\osx-x64\demo" />
<file src="dummyshim" target="tools\netcoreapp2.1\any\shims\linux\demo" />
</files>
</package>

View file

@ -0,0 +1,130 @@
// 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 System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Transactions;
using FluentAssertions;
using Microsoft.DotNet.Tools.Test.Utilities;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools;
using Microsoft.DotNet.Tools.Tool.Install;
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
{
public class ToolPackageInstanceTests : TestBase
{
[Theory]
[InlineData(false)]
[InlineData(true)]
public void GivenAnInstalledPackageUninstallRemovesThePackage(bool testMockBehaviorIsInSync)
{
var source = GetTestLocalFeedPath();
var (store, installer, reporter, fileSystem) = Setup(
useMock: testMockBehaviorIsInSync,
feeds: GetMockFeedsForSource(source));
var package = installer.InstallPackage(
packageId: TestPackageId,
versionRange: VersionRange.Parse(TestPackageVersion),
targetFramework: _testTargetframework,
additionalFeeds: new[] {source});
package.PackagedShims.Should().ContainSingle(f => f.Value.Contains("demo.exe") || f.Value.Contains("demo"));
package.Uninstall();
}
private static FilePath GetUniqueTempProjectPathEachTest()
{
var tempProjectDirectory =
new DirectoryPath(Path.GetTempPath()).WithSubDirectories(Path.GetRandomFileName());
var tempProjectPath =
tempProjectDirectory.WithFile(Path.GetRandomFileName() + ".csproj");
return tempProjectPath;
}
private static IEnumerable<MockFeed> GetMockFeedsForSource(string source)
{
return new[]
{
new MockFeed
{
Type = MockFeedType.ImplicitAdditionalFeed,
Uri = source,
Packages = new List<MockFeedPackage>
{
new MockFeedPackage
{
PackageId = TestPackageId.ToString(),
Version = TestPackageVersion
}
}
}
};
}
private static (IToolPackageStore, IToolPackageInstaller, BufferedReporter, IFileSystem) Setup(
bool useMock,
IEnumerable<MockFeed> feeds = null,
FilePath? tempProject = null,
DirectoryPath? offlineFeed = null)
{
var root = new DirectoryPath(Path.Combine(TempRoot.Root, Path.GetRandomFileName()));
var reporter = new BufferedReporter();
IFileSystem fileSystem;
IToolPackageStore store;
IToolPackageInstaller installer;
if (useMock)
{
var packagedShimsMap = new Dictionary<PackageId, IReadOnlyList<FilePath>>
{
[TestPackageId] = new FilePath[] {new FilePath("path/demo.exe")}
};
fileSystem = new FileSystemMockBuilder().Build();
store = new ToolPackageStoreMock(root, fileSystem);
installer = new ToolPackageInstallerMock(
fileSystem: fileSystem,
store: store,
projectRestorer: new ProjectRestorerMock(
fileSystem: fileSystem,
reporter: reporter,
feeds: feeds),
packagedShimsMap: packagedShimsMap);
}
else
{
fileSystem = new FileSystemWrapper();
store = new ToolPackageStore(root);
installer = new ToolPackageInstaller(
store: store,
projectRestorer: new ProjectRestorer(reporter),
tempProject: tempProject ?? GetUniqueTempProjectPathEachTest(),
offlineFeed: offlineFeed ?? new DirectoryPath("does not exist"));
}
store.Root.Value.Should().Be(Path.GetFullPath(root.Value));
return (store, installer, reporter, fileSystem);
}
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 static readonly PackageId TestPackageId = new PackageId("global.tool.console.demo.with.shim");
}
}

View file

@ -0,0 +1,38 @@
// 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 Microsoft.DotNet.ShellShim;
using Microsoft.Extensions.EnvironmentAbstractions;
using Newtonsoft.Json;
namespace Microsoft.DotNet.Tools.Tests.ComponentMocks
{
internal class AppHostShellShimMakerMock : IAppHostShellShimMaker
{
private static IFileSystem _fileSystem;
public AppHostShellShimMakerMock(IFileSystem fileSystem = null)
{
_fileSystem = fileSystem ?? new FileSystemWrapper();
}
public void CreateApphostShellShim(FilePath entryPoint, FilePath shimPath)
{
var shim = new FakeShim
{
Runner = "dotnet",
ExecutablePath = entryPoint.Value
};
_fileSystem.File.WriteAllText(
shimPath.Value,
JsonConvert.SerializeObject(shim));
}
public class FakeShim
{
public string Runner { get; set; }
public string ExecutablePath { get; set; }
}
}
}

View file

@ -1,104 +0,0 @@
// 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 System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ShellShim;
using Microsoft.Extensions.EnvironmentAbstractions;
using Newtonsoft.Json;
namespace Microsoft.DotNet.Tools.Tests.ComponentMocks
{
internal class ShellShimRepositoryMock : IShellShimRepository
{
private static IFileSystem _fileSystem;
private readonly DirectoryPath _pathToPlaceShim;
public ShellShimRepositoryMock(DirectoryPath pathToPlaceShim, IFileSystem fileSystem = null)
{
_pathToPlaceShim = pathToPlaceShim;
_fileSystem = fileSystem ?? new FileSystemWrapper();
}
public void CreateShim(FilePath targetExecutablePath, string commandName)
{
if (ShimExists(commandName))
{
throw new ShellShimException(
string.Format(CommonLocalizableStrings.ShellShimConflict,
commandName));
}
TransactionalAction.Run(
action: () => {
var shim = new FakeShim
{
Runner = "dotnet",
ExecutablePath = targetExecutablePath.Value
};
_fileSystem.File.WriteAllText(
GetShimPath(commandName).Value,
JsonConvert.SerializeObject(shim));
},
rollback: () => {
_fileSystem.File.Delete(GetShimPath(commandName).Value);
});
}
public void RemoveShim(string commandName)
{
var originalShimPath = GetShimPath(commandName);
if (!_fileSystem.File.Exists(originalShimPath.Value))
{
return;
}
string tempShimPath = null;
TransactionalAction.Run(
action: () => {
var tempFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
_fileSystem.File.Move(originalShimPath.Value, tempFile);
tempShimPath = tempFile;
},
commit: () => {
if (tempShimPath != null)
{
_fileSystem.File.Delete(tempShimPath);
}
},
rollback: () => {
if (tempShimPath != null)
{
_fileSystem.File.Move(tempShimPath, originalShimPath.Value);
}
});
}
private bool ShimExists(string commandName)
{
return _fileSystem.File.Exists(GetShimPath(commandName).Value);
}
private FilePath GetShimPath(string shellCommandName)
{
var shimPath = Path.Combine(_pathToPlaceShim.Value, shellCommandName);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
shimPath += ".exe";
}
return new FilePath(shimPath);
}
public class FakeShim
{
public string Runner { get; set; }
public string ExecutablePath { get; set; }
}
}
}

View file

@ -23,19 +23,22 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly Action _installCallback; private readonly Action _installCallback;
private readonly Dictionary<PackageId, IEnumerable<string>> _warningsMap; private readonly Dictionary<PackageId, IEnumerable<string>> _warningsMap;
private readonly Dictionary<PackageId, IReadOnlyList<FilePath>> _packagedShimsMap;
public ToolPackageInstallerMock( public ToolPackageInstallerMock(
IFileSystem fileSystem, IFileSystem fileSystem,
IToolPackageStore store, IToolPackageStore store,
IProjectRestorer projectRestorer, IProjectRestorer projectRestorer,
Action installCallback = null, Action installCallback = null,
Dictionary<PackageId, IEnumerable<string>> warningsMap = null) Dictionary<PackageId, IEnumerable<string>> warningsMap = null,
Dictionary<PackageId, IReadOnlyList<FilePath>> packagedShimsMap = null)
{ {
_fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
_store = store ?? throw new ArgumentNullException(nameof(store)); _store = store ?? throw new ArgumentNullException(nameof(store));
_projectRestorer = projectRestorer ?? throw new ArgumentNullException(nameof(projectRestorer)); _projectRestorer = projectRestorer ?? throw new ArgumentNullException(nameof(projectRestorer));
_installCallback = installCallback; _installCallback = installCallback;
_warningsMap = warningsMap ?? new Dictionary<PackageId, IEnumerable<string>>(); _warningsMap = warningsMap ?? new Dictionary<PackageId, IEnumerable<string>>();
_packagedShimsMap = packagedShimsMap ?? new Dictionary<PackageId, IReadOnlyList<FilePath>>();
} }
public IToolPackage InstallPackage(PackageId packageId, public IToolPackage InstallPackage(PackageId packageId,
@ -92,7 +95,10 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks
IEnumerable<string> warnings = null; IEnumerable<string> warnings = null;
_warningsMap.TryGetValue(packageId, out warnings); _warningsMap.TryGetValue(packageId, out warnings);
return new ToolPackageMock(_fileSystem, packageId, version, packageDirectory, warnings: warnings); IReadOnlyList<FilePath> packedShims = null;
_packagedShimsMap.TryGetValue(packageId, out packedShims);
return new ToolPackageMock(_fileSystem, packageId, version, packageDirectory, warnings: warnings, packagedShims: packedShims);
}, },
rollback: () => { rollback: () => {
if (rollbackDirectory != null && _fileSystem.Directory.Exists(rollbackDirectory)) if (rollbackDirectory != null && _fileSystem.Directory.Exists(rollbackDirectory))

View file

@ -18,6 +18,7 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks
private Lazy<IReadOnlyList<CommandSettings>> _commands; private Lazy<IReadOnlyList<CommandSettings>> _commands;
private Action _uninstallCallback; private Action _uninstallCallback;
private IEnumerable<string> _warnings; private IEnumerable<string> _warnings;
private readonly IReadOnlyList<FilePath> _packagedShims;
public ToolPackageMock( public ToolPackageMock(
IFileSystem fileSystem, IFileSystem fileSystem,
@ -25,7 +26,8 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks
NuGetVersion version, NuGetVersion version,
DirectoryPath packageDirectory, DirectoryPath packageDirectory,
Action uninstallCallback = null, Action uninstallCallback = null,
IEnumerable<string> warnings = null) IEnumerable<string> warnings = null,
IReadOnlyList<FilePath> packagedShims = null)
{ {
_fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem)); _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
Id = id; Id = id;
@ -34,6 +36,7 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks
_commands = new Lazy<IReadOnlyList<CommandSettings>>(GetCommands); _commands = new Lazy<IReadOnlyList<CommandSettings>>(GetCommands);
_uninstallCallback = uninstallCallback; _uninstallCallback = uninstallCallback;
_warnings = warnings ?? new List<string>(); _warnings = warnings ?? new List<string>();
_packagedShims = packagedShims ?? new List<FilePath>();
} }
public PackageId Id { get; private set; } public PackageId Id { get; private set; }
@ -52,6 +55,14 @@ namespace Microsoft.DotNet.Tools.Tests.ComponentMocks
public IEnumerable<string> Warnings => _warnings; public IEnumerable<string> Warnings => _warnings;
public IReadOnlyList<FilePath> PackagedShims
{
get
{
return _packagedShims;
}
}
public void Uninstall() public void Uninstall()
{ {
var rootDirectory = PackageDirectory.GetParentPath(); var rootDirectory = PackageDirectory.GetParentPath();

View file

@ -132,6 +132,20 @@ namespace Microsoft.Extensions.DependencyModel.Tests
_files.Remove(path); _files.Remove(path);
} }
public void Copy(string source, string destination)
{
if (!Exists(source))
{
throw new FileNotFoundException("source does not exist.");
}
if (Exists(destination))
{
throw new IOException("destination exists.");
}
_files[destination] = _files[source];
}
} }
private class DirectoryMock : IDirectory private class DirectoryMock : IDirectory

View file

@ -46,7 +46,11 @@ namespace Microsoft.DotNet.Tests.Commands
_fileSystem = new FileSystemMockBuilder().Build(); _fileSystem = new FileSystemMockBuilder().Build();
_toolPackageStore = new ToolPackageStoreMock(new DirectoryPath(PathToPlacePackages), _fileSystem); _toolPackageStore = new ToolPackageStoreMock(new DirectoryPath(PathToPlacePackages), _fileSystem);
_createShellShimRepository = _createShellShimRepository =
(nonGlobalLocation) => new ShellShimRepositoryMock(new DirectoryPath(PathToPlaceShim), _fileSystem); (nonGlobalLocation) => new ShellShimRepository(
new DirectoryPath(PathToPlaceShim),
fileSystem: _fileSystem,
appHostShellShimMaker: new AppHostShellShimMakerMock(_fileSystem),
filePermissionSetter: new NoOpFilePermissionSetter());
_environmentPathInstructionMock = _environmentPathInstructionMock =
new EnvironmentPathInstructionMock(_reporter, PathToPlaceShim); new EnvironmentPathInstructionMock(_reporter, PathToPlaceShim);
_createToolPackageStoreAndInstaller = (_) => (_toolPackageStore, CreateToolPackageInstaller()); _createToolPackageStoreAndInstaller = (_) => (_toolPackageStore, CreateToolPackageInstaller());
@ -71,7 +75,7 @@ namespace Microsoft.DotNet.Tests.Commands
// It is hard to simulate shell behavior. Only Assert shim can point to executable dll // It is hard to simulate shell behavior. Only Assert shim can point to executable dll
_fileSystem.File.Exists(ExpectedCommandPath()).Should().BeTrue(); _fileSystem.File.Exists(ExpectedCommandPath()).Should().BeTrue();
var deserializedFakeShim = JsonConvert.DeserializeObject<ShellShimRepositoryMock.FakeShim>( var deserializedFakeShim = JsonConvert.DeserializeObject<AppHostShellShimMakerMock.FakeShim>(
_fileSystem.File.ReadAllText(ExpectedCommandPath())); _fileSystem.File.ReadAllText(ExpectedCommandPath()));
_fileSystem.File.Exists(deserializedFakeShim.ExecutablePath).Should().BeTrue(); _fileSystem.File.Exists(deserializedFakeShim.ExecutablePath).Should().BeTrue();
@ -117,7 +121,7 @@ namespace Microsoft.DotNet.Tests.Commands
_fileSystem.File.Exists(ExpectedCommandPath()) _fileSystem.File.Exists(ExpectedCommandPath())
.Should().BeTrue(); .Should().BeTrue();
var deserializedFakeShim = var deserializedFakeShim =
JsonConvert.DeserializeObject<ShellShimRepositoryMock.FakeShim>( JsonConvert.DeserializeObject<AppHostShellShimMakerMock.FakeShim>(
_fileSystem.File.ReadAllText(ExpectedCommandPath())); _fileSystem.File.ReadAllText(ExpectedCommandPath()));
_fileSystem.File.Exists(deserializedFakeShim.ExecutablePath).Should().BeTrue(); _fileSystem.File.Exists(deserializedFakeShim.ExecutablePath).Should().BeTrue();
} }
@ -455,6 +459,42 @@ namespace Microsoft.DotNet.Tests.Commands
_reporter.Lines.Should().NotContain(l => l.Contains(EnvironmentPathInstructionMock.MockInstructionText)); _reporter.Lines.Should().NotContain(l => l.Contains(EnvironmentPathInstructionMock.MockInstructionText));
} }
[Fact]
public void AndPackagedShimIsProvidedWhenRunWithPackageIdItCreateShimUsingPackagedShim()
{
var extension = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".exe" : string.Empty;
var prepackagedShimPath = "packagedShimDirectory/" + ProjectRestorerMock.FakeCommandName + extension;
var tokenToIdentifyPackagedShim = "packagedShim";
_fileSystem.File.WriteAllText(prepackagedShimPath, tokenToIdentifyPackagedShim);
var result = Parser.Instance.Parse($"dotnet tool install --tool-path /tmp/folder {PackageId}");
var appliedCommand = result["dotnet"]["tool"]["install"];
var parser = Parser.Instance;
var parseResult = parser.ParseFrom("dotnet tool", new[] {"install", "-g", PackageId});
var packagedShimsMap = new Dictionary<PackageId, IReadOnlyList<FilePath>>
{
[new PackageId(PackageId)] = new[] {new FilePath(prepackagedShimPath)}
};
var installCommand = new ToolInstallCommand(appliedCommand,
parseResult,
(_) => (_toolPackageStore, new ToolPackageInstallerMock(
fileSystem: _fileSystem,
store: _toolPackageStore,
packagedShimsMap: packagedShimsMap,
projectRestorer: new ProjectRestorerMock(
fileSystem: _fileSystem,
reporter: _reporter))),
_createShellShimRepository,
new EnvironmentPathInstructionMock(_reporter, PathToPlaceShim),
_reporter);
installCommand.Execute().Should().Be(0);
_fileSystem.File.ReadAllText(ExpectedCommandPath()).Should().Be(tokenToIdentifyPackagedShim);
}
private IToolPackageInstaller CreateToolPackageInstaller( private IToolPackageInstaller CreateToolPackageInstaller(
IEnumerable<MockFeed> feeds = null, IEnumerable<MockFeed> feeds = null,
Action installCallback = null) Action installCallback = null)
@ -476,5 +516,12 @@ namespace Microsoft.DotNet.Tests.Commands
"pathToPlace", "pathToPlace",
ProjectRestorerMock.FakeCommandName + extension); ProjectRestorerMock.FakeCommandName + extension);
} }
private class NoOpFilePermissionSetter : IFilePermissionSetter
{
public void SetUserExecutionPermission(string path)
{
}
}
} }
} }

View file

@ -22,6 +22,7 @@ using Xunit;
using Parser = Microsoft.DotNet.Cli.Parser; using Parser = Microsoft.DotNet.Cli.Parser;
using LocalizableStrings = Microsoft.DotNet.Tools.Tool.Uninstall.LocalizableStrings; using LocalizableStrings = Microsoft.DotNet.Tools.Tool.Uninstall.LocalizableStrings;
using InstallLocalizableStrings = Microsoft.DotNet.Tools.Tool.Install.LocalizableStrings; using InstallLocalizableStrings = Microsoft.DotNet.Tools.Tool.Install.LocalizableStrings;
using Microsoft.DotNet.ShellShim;
namespace Microsoft.DotNet.Tests.Commands namespace Microsoft.DotNet.Tests.Commands
{ {
@ -203,7 +204,10 @@ namespace Microsoft.DotNet.Tests.Commands
result["dotnet"]["tool"]["install"], result["dotnet"]["tool"]["install"],
result, result,
(_) => (store, packageInstallerMock), (_) => (store, packageInstallerMock),
(_) => new ShellShimRepositoryMock(new DirectoryPath(ShimsDirectory), _fileSystem), (_) => new ShellShimRepository(
new DirectoryPath(ShimsDirectory),
fileSystem: _fileSystem,
appHostShellShimMaker: new AppHostShellShimMakerMock(_fileSystem)),
_environmentPathInstructionMock, _environmentPathInstructionMock,
_reporter); _reporter);
} }
@ -219,7 +223,10 @@ namespace Microsoft.DotNet.Tests.Commands
new DirectoryPath(ToolsDirectory), new DirectoryPath(ToolsDirectory),
_fileSystem, _fileSystem,
uninstallCallback), uninstallCallback),
(_) => new ShellShimRepositoryMock(new DirectoryPath(ShimsDirectory), _fileSystem), (_) => new ShellShimRepository(
new DirectoryPath(ShimsDirectory),
fileSystem: _fileSystem,
appHostShellShimMaker: new AppHostShellShimMakerMock(_fileSystem)),
_reporter); _reporter);
} }
} }

View file

@ -17,6 +17,7 @@ using Microsoft.Extensions.EnvironmentAbstractions;
using Xunit; using Xunit;
using Parser = Microsoft.DotNet.Cli.Parser; using Parser = Microsoft.DotNet.Cli.Parser;
using LocalizableStrings = Microsoft.DotNet.Tools.Tool.Update.LocalizableStrings; using LocalizableStrings = Microsoft.DotNet.Tools.Tool.Update.LocalizableStrings;
using Microsoft.DotNet.ShellShim;
namespace Microsoft.DotNet.Tests.Commands namespace Microsoft.DotNet.Tests.Commands
{ {
@ -139,7 +140,7 @@ namespace Microsoft.DotNet.Tests.Commands
_mockFeeds _mockFeeds
), ),
installCallback: () => throw new ToolConfigurationException("Simulated error"))), installCallback: () => throw new ToolConfigurationException("Simulated error"))),
_ => new ShellShimRepositoryMock(new DirectoryPath(ShimsDirectory), _fileSystem), _ => GetMockedShellShimRepository(),
_reporter); _reporter);
Action a = () => command.Execute(); Action a = () => command.Execute();
@ -168,7 +169,7 @@ namespace Microsoft.DotNet.Tests.Commands
_mockFeeds _mockFeeds
), ),
installCallback: () => throw new ToolConfigurationException("Simulated error"))), installCallback: () => throw new ToolConfigurationException("Simulated error"))),
_ => new ShellShimRepositoryMock(new DirectoryPath(ShimsDirectory), _fileSystem), _ => GetMockedShellShimRepository(),
_reporter); _reporter);
Action a = () => command.Execute(); Action a = () => command.Execute();
@ -216,7 +217,7 @@ namespace Microsoft.DotNet.Tests.Commands
_reporter, _reporter,
_mockFeeds _mockFeeds
))), ))),
(_) => new ShellShimRepositoryMock(new DirectoryPath(ShimsDirectory), _fileSystem), (_) => GetMockedShellShimRepository(),
_environmentPathInstructionMock, _environmentPathInstructionMock,
_reporter); _reporter);
} }
@ -236,8 +237,16 @@ namespace Microsoft.DotNet.Tests.Commands
_reporter, _reporter,
_mockFeeds _mockFeeds
))), ))),
(_) => new ShellShimRepositoryMock(new DirectoryPath(ShimsDirectory), _fileSystem), (_) => GetMockedShellShimRepository(),
_reporter); _reporter);
} }
private ShellShimRepository GetMockedShellShimRepository()
{
return new ShellShimRepository(
new DirectoryPath(ShimsDirectory),
fileSystem: _fileSystem,
appHostShellShimMaker: new AppHostShellShimMakerMock(_fileSystem));
}
} }
} }