Make command searcher not use wildcard search for execution (#9202)
This commit is contained in:
parent
b7c7aa176e
commit
5e4b4d1351
12
build.psm1
12
build.psm1
|
@ -907,7 +907,10 @@ function Start-PSPester {
|
|||
[switch]$IncludeCommonTests,
|
||||
[string]$ExperimentalFeatureName,
|
||||
[Parameter(HelpMessage='Title to publish the results as.')]
|
||||
[string]$Title = 'PowerShell Core Tests'
|
||||
[string]$Title = 'PowerShell Core Tests',
|
||||
[Parameter(ParameterSetName='Wait', Mandatory=$true,
|
||||
HelpMessage='Wait for the debugger to attach to PowerShell before Pester starts. Debug builds only!')]
|
||||
[switch]$Wait
|
||||
)
|
||||
|
||||
if (-not (Get-Module -ListAvailable -Name $Pester -ErrorAction SilentlyContinue | Where-Object { $_.Version -ge "4.2" } ))
|
||||
|
@ -1101,6 +1104,13 @@ function Start-PSPester {
|
|||
$PSFlags = @("-settings", $configFile, "-noprofile")
|
||||
}
|
||||
|
||||
# -Wait is only available on Debug builds
|
||||
# It is used to allow the debugger to attach before PowerShell
|
||||
# runs pester in this case
|
||||
if($Wait.IsPresent){
|
||||
$PSFlags += '-wait'
|
||||
}
|
||||
|
||||
# To ensure proper testing, the module path must not be inherited by the spawned process
|
||||
try {
|
||||
$originalModulePath = $env:PSModulePath
|
||||
|
|
|
@ -735,7 +735,7 @@ namespace System.Management.Automation
|
|||
|
||||
internal static CommandInfo LookupCommandInfo(string commandName, CommandOrigin commandOrigin, ExecutionContext context)
|
||||
{
|
||||
return LookupCommandInfo(commandName, CommandTypes.All, SearchResolutionOptions.None, commandOrigin, context);
|
||||
return LookupCommandInfo(commandName, CommandTypes.All, SearchResolutionOptions.ResolveLiteralThenPathPatterns, commandOrigin, context);
|
||||
}
|
||||
|
||||
internal static CommandInfo LookupCommandInfo(
|
||||
|
|
|
@ -456,52 +456,33 @@ namespace System.Management.Automation
|
|||
"Trying to resolve the path as an PSPath");
|
||||
|
||||
// Find the match if it is.
|
||||
// Try literal path resolution if it is set to run first
|
||||
if (_commandResolutionOptions.HasFlag(SearchResolutionOptions.ResolveLiteralThenPathPatterns))
|
||||
{
|
||||
var path = GetNextLiteralPathThatExists(_commandName, out _);
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
return GetInfoFromPath(path);
|
||||
}
|
||||
}
|
||||
|
||||
Collection<string> resolvedPaths = new Collection<string>();
|
||||
if (WildcardPattern.ContainsWildcardCharacters(_commandName))
|
||||
{
|
||||
resolvedPaths = GetNextFromPathUsingWildcards(_commandName, out _);
|
||||
}
|
||||
|
||||
try
|
||||
// Try literal path resolution if wildcards are enable first and wildcard search failed
|
||||
if (!_commandResolutionOptions.HasFlag(SearchResolutionOptions.ResolveLiteralThenPathPatterns) &&
|
||||
resolvedPaths.Count == 0)
|
||||
{
|
||||
Provider.CmdletProvider providerInstance;
|
||||
ProviderInfo provider;
|
||||
resolvedPaths =
|
||||
_context.LocationGlobber.GetGlobbedProviderPathsFromMonadPath(_commandName, false, out provider, out providerInstance);
|
||||
}
|
||||
catch (ItemNotFoundException)
|
||||
{
|
||||
CommandDiscovery.discoveryTracer.TraceError(
|
||||
"The path could not be found: {0}",
|
||||
_commandName);
|
||||
}
|
||||
catch (DriveNotFoundException)
|
||||
{
|
||||
CommandDiscovery.discoveryTracer.TraceError(
|
||||
"A drive could not be found for the path: {0}",
|
||||
_commandName);
|
||||
}
|
||||
catch (ProviderNotFoundException)
|
||||
{
|
||||
CommandDiscovery.discoveryTracer.TraceError(
|
||||
"A provider could not be found for the path: {0}",
|
||||
_commandName);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
CommandDiscovery.discoveryTracer.TraceError(
|
||||
"The path specified a home directory, but the provider home directory was not set. {0}",
|
||||
_commandName);
|
||||
}
|
||||
catch (ProviderInvocationException providerException)
|
||||
{
|
||||
CommandDiscovery.discoveryTracer.TraceError(
|
||||
"The provider associated with the path '{0}' encountered an error: {1}",
|
||||
_commandName,
|
||||
providerException.Message);
|
||||
}
|
||||
catch (PSNotSupportedException)
|
||||
{
|
||||
CommandDiscovery.discoveryTracer.TraceError(
|
||||
"The provider associated with the path '{0}' does not implement ContainerCmdletProvider",
|
||||
_commandName);
|
||||
string path = GetNextLiteralPathThatExists(_commandName, out _);
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
return GetInfoFromPath(path);
|
||||
}
|
||||
}
|
||||
|
||||
if (resolvedPaths.Count > 1)
|
||||
|
@ -528,6 +509,64 @@ namespace System.Management.Automation
|
|||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next path using WildCards.
|
||||
/// </summary>
|
||||
/// <param name="command">
|
||||
/// The command to search for.
|
||||
/// </param>
|
||||
/// <param name="provider">The provider that the command was found in.</param>
|
||||
/// <returns>
|
||||
/// A collection of full paths to the commands which were found.
|
||||
/// </returns>
|
||||
private Collection<string> GetNextFromPathUsingWildcards(string command, out ProviderInfo provider)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _context.LocationGlobber.GetGlobbedProviderPathsFromMonadPath(path: command, allowNonexistingPaths: false, provider: out provider, providerInstance: out _);
|
||||
}
|
||||
catch (ItemNotFoundException)
|
||||
{
|
||||
CommandDiscovery.discoveryTracer.TraceError(
|
||||
"The path could not be found: {0}",
|
||||
command);
|
||||
}
|
||||
catch (DriveNotFoundException)
|
||||
{
|
||||
CommandDiscovery.discoveryTracer.TraceError(
|
||||
"A drive could not be found for the path: {0}",
|
||||
command);
|
||||
}
|
||||
catch (ProviderNotFoundException)
|
||||
{
|
||||
CommandDiscovery.discoveryTracer.TraceError(
|
||||
"A provider could not be found for the path: {0}",
|
||||
command);
|
||||
}
|
||||
catch (InvalidOperationException)
|
||||
{
|
||||
CommandDiscovery.discoveryTracer.TraceError(
|
||||
"The path specified a home directory, but the provider home directory was not set. {0}",
|
||||
command);
|
||||
}
|
||||
catch (ProviderInvocationException providerException)
|
||||
{
|
||||
CommandDiscovery.discoveryTracer.TraceError(
|
||||
"The provider associated with the path '{0}' encountered an error: {1}",
|
||||
command,
|
||||
providerException.Message);
|
||||
}
|
||||
catch (PSNotSupportedException)
|
||||
{
|
||||
CommandDiscovery.discoveryTracer.TraceError(
|
||||
"The provider associated with the path '{0}' does not implement ContainerCmdletProvider",
|
||||
command);
|
||||
}
|
||||
|
||||
provider = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static bool checkPath(string path, string commandName)
|
||||
{
|
||||
return path.StartsWith(commandName, StringComparison.OrdinalIgnoreCase);
|
||||
|
@ -1092,15 +1131,21 @@ namespace System.Management.Automation
|
|||
{
|
||||
ProviderInfo provider = null;
|
||||
string resolvedPath = null;
|
||||
if (WildcardPattern.ContainsWildcardCharacters(path))
|
||||
|
||||
// Try literal path resolution if it is set to run first
|
||||
if (_commandResolutionOptions.HasFlag(SearchResolutionOptions.ResolveLiteralThenPathPatterns))
|
||||
{
|
||||
// Cannot return early as this code path only expects
|
||||
// The file system provider and the final check for that
|
||||
// must verify this before we return.
|
||||
resolvedPath = GetNextLiteralPathThatExists(path, out provider);
|
||||
}
|
||||
|
||||
if (WildcardPattern.ContainsWildcardCharacters(path) &&
|
||||
((resolvedPath == null) || (provider == null)))
|
||||
{
|
||||
// Let PowerShell resolve relative path with wildcards.
|
||||
Provider.CmdletProvider providerInstance;
|
||||
Collection<string> resolvedPaths = _context.LocationGlobber.GetGlobbedProviderPathsFromMonadPath(
|
||||
path,
|
||||
false,
|
||||
out provider,
|
||||
out providerInstance);
|
||||
Collection<string> resolvedPaths = GetNextFromPathUsingWildcards(path, out provider);
|
||||
|
||||
if (resolvedPaths.Count == 0)
|
||||
{
|
||||
|
@ -1124,14 +1169,15 @@ namespace System.Management.Automation
|
|||
}
|
||||
}
|
||||
|
||||
// Revert to previous path resolver if wildcards produces no results.
|
||||
if ((resolvedPath == null) || (provider == null))
|
||||
// Try literal path resolution if wildcards are enabled first and wildcard search failed
|
||||
if (!_commandResolutionOptions.HasFlag(SearchResolutionOptions.ResolveLiteralThenPathPatterns) &&
|
||||
((resolvedPath == null) || (provider == null)))
|
||||
{
|
||||
resolvedPath = _context.LocationGlobber.GetProviderPath(path, out provider);
|
||||
resolvedPath = GetNextLiteralPathThatExists(path, out provider);
|
||||
}
|
||||
|
||||
// Verify the path was resolved to a file system path
|
||||
if (provider.NameEquals(_context.ProviderNames.FileSystem))
|
||||
if (provider != null && provider.NameEquals(_context.ProviderNames.FileSystem))
|
||||
{
|
||||
result = resolvedPath;
|
||||
|
||||
|
@ -1176,6 +1222,32 @@ namespace System.Management.Automation
|
|||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the next literal path.
|
||||
/// Filtering to ones that exist for the filesystem.
|
||||
/// </summary>
|
||||
/// <param name="command">
|
||||
/// The command to search for.
|
||||
/// </param>
|
||||
/// <param name="provider">The provider that the command was found in.</param>
|
||||
/// <returns>
|
||||
/// Full path to the command.
|
||||
/// </returns>
|
||||
private string GetNextLiteralPathThatExists(string command, out ProviderInfo provider)
|
||||
{
|
||||
string resolvedPath = _context.LocationGlobber.GetProviderPath(command, out provider);
|
||||
|
||||
if (provider.NameEquals(_context.ProviderNames.FileSystem)
|
||||
&& !File.Exists(resolvedPath)
|
||||
&& !Directory.Exists(resolvedPath))
|
||||
{
|
||||
provider = null;
|
||||
return null;
|
||||
}
|
||||
|
||||
return resolvedPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a collection of patterns used to find the command.
|
||||
/// </summary>
|
||||
|
@ -1609,5 +1681,10 @@ namespace System.Management.Automation
|
|||
/// Enable searching for cmdlets/functions by abbreviation expansion.
|
||||
/// </summary>
|
||||
UseAbbreviationExpansion = 0x20,
|
||||
|
||||
/// <summary>
|
||||
/// Enable resolving wildcard in paths.
|
||||
/// </summary>
|
||||
ResolveLiteralThenPathPatterns = 0x40
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,18 +85,107 @@ Describe "Command Discovery tests" -Tags "CI" {
|
|||
(& 'location').Path | Should -Be (get-location).Path
|
||||
}
|
||||
|
||||
Context "Get-Command should use globbing for scripts" {
|
||||
Context "Use literal path first when executing scripts" {
|
||||
BeforeAll {
|
||||
$firstFileName = '[test1].ps1'
|
||||
$secondFileName = '1.ps1'
|
||||
$thirdFileName = '2.ps1'
|
||||
$firstResult = "executing $firstFileName in root"
|
||||
$secondResult = "executing $secondFileName in root"
|
||||
$thirdResult = "executing $thirdFileName in root"
|
||||
setup -f $firstFileName -content "'$firstResult'"
|
||||
setup -f $secondFileName -content "'$secondResult'"
|
||||
setup -f $thirdFileName -content "'$thirdResult'"
|
||||
|
||||
$subFolder = 'subFolder'
|
||||
$firstFileInSubFolder = Join-Path $subFolder -ChildPath $firstFileName
|
||||
$secondFileInSubFolder = Join-Path $subFolder -ChildPath $secondFileName
|
||||
$thirdFileInSubFolder = Join-Path $subFolder -ChildPath $thirdFileName
|
||||
setup -f $firstFileInSubFolder -content "'$firstResult'"
|
||||
setup -f $secondFileInSubFolder -content "'$secondResult'"
|
||||
setup -f $thirdFileInSubFolder -content "'$thirdResult'"
|
||||
|
||||
$secondFileSearchInSubfolder = (Join-Path -Path $subFolder -ChildPath '[t1].ps1')
|
||||
|
||||
$executionWithWildcardCases = @(
|
||||
#Region relative paths with './'
|
||||
@{command = '.\[test1].ps1' ; expectedResult = $firstResult; name = '.\[test1].ps1'}
|
||||
@{command = '.\[t1].ps1' ; expectedResult = $secondResult; name = '.\[t1].ps1'}
|
||||
#endregion
|
||||
|
||||
#Region relative Subfolder paths without './'
|
||||
@{command = $secondFileInSubFolder ; expectedResult = $secondResult; name = $secondFileInSubFolder}
|
||||
|
||||
# Wildcard search is not being performed in this scenario before this change.
|
||||
# I noted the issue in the pending message
|
||||
@{command = $firstFileInSubFolder ; expectedResult = $firstResult; name = $firstFileInSubFolder; Pending="See note about wildcard in https://github.com/PowerShell/PowerShell/issues/9256"}
|
||||
@{command = $secondFileSearchInSubfolder ; expectedResult = $secondResult; name = $secondFileSearchInSubfolder; Pending="See note about wildcard in https://github.com/PowerShell/PowerShell/issues/9256"}
|
||||
#endregion
|
||||
#Region relative Subfolder paths with '.\'
|
||||
@{command = '.\' + $secondFileInSubFolder ; expectedResult = $secondResult; name = $secondFileInSubFolder}
|
||||
@{command = '.\subFolder\[test1].ps1' ; expectedResult = $firstResult; name = '.\subFolder\[test1].ps1'}
|
||||
@{command = '.\subFolder\[t1].ps1' ; expectedResult = $secondResult; name = '.\' + $secondFileSearchInSubfolder}
|
||||
@{command = '.\' + $firstFileInSubFolder ; expectedResult = $firstResult; name = '.\' + $firstFileInSubFolder}
|
||||
@{command = '.\' + $secondFileSearchInSubfolder ; expectedResult = $secondResult; name = '.\' + $secondFileSearchInSubfolder}
|
||||
#endregion
|
||||
|
||||
#region rooted paths
|
||||
@{command = (Join-Path ${TestDrive} -ChildPath '[test1].ps1') ; expectedResult = $firstResult; name = '.\[test1].ps1 by fully qualified path'}
|
||||
@{command = (Join-Path ${TestDrive} -ChildPath '[t1].ps1') ; expectedResult = $secondResult; name = '.\1.ps1 by fully qualified path with wildcard'}
|
||||
#endregion
|
||||
)
|
||||
|
||||
$shouldNotExecuteCases = @(
|
||||
@{command = 'subFolder\[test1].ps1' ; testName = 'Relative path that where module qualified syntax overlaps'; ExpectedErrorId = 'CouldNotAutoLoadModule'}
|
||||
@{command = '.\[12].ps1' ; testName = 'relative path with bracket wildcard matctching multiple files'}
|
||||
@{command = (Join-Path ${TestDrive} -ChildPath '[12].ps1') ; testName = 'fully qualified path with bracket wildcard matching multiple files'}
|
||||
)
|
||||
|
||||
Push-Location ${TestDrive}\
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
Pop-Location
|
||||
}
|
||||
|
||||
It "Invoking <name> should return '<expectedResult>'" -TestCases $executionWithWildcardCases {
|
||||
param($command, $expectedResult, [String]$Pending)
|
||||
|
||||
if($Pending)
|
||||
{
|
||||
Set-TestInconclusive -Message $Pending
|
||||
}
|
||||
|
||||
& $command | Should -BeExactly $expectedResult
|
||||
}
|
||||
|
||||
It "'<testName>' should not execute" -TestCases $shouldNotExecuteCases {
|
||||
param(
|
||||
[string]
|
||||
$command,
|
||||
[string]
|
||||
$ExpectedErrorId = 'CommandNotFoundException'
|
||||
)
|
||||
{ & $command } | Should -Throw -ErrorId $ExpectedErrorId
|
||||
}
|
||||
}
|
||||
|
||||
Context "Get-Command should use globbing first for scripts" {
|
||||
BeforeAll {
|
||||
$firstResult = '[first script]'
|
||||
$secondResult = 'alt script'
|
||||
$thirdResult = 'bad script'
|
||||
setup -f '[test1].ps1' -content "'$firstResult'"
|
||||
setup -f '1.ps1' -content "'$secondResult'"
|
||||
setup -f '2.ps1' -content "'$thirdResult'"
|
||||
|
||||
$gcmWithWildcardCases = @(
|
||||
@{command = '.\?[tb]est1?.ps1'; expectedCommand = '[test1].ps1'; expectedCommandCount =1; name = '''.\?[tb]est1?.ps1'''}
|
||||
@{command = (Join-Path ${TestDrive} -ChildPath '?[tb]est1?.ps1'); expectedCommand = '[test1].ps1'; expectedCommandCount =1 ; name = '''.\?[tb]est1?.ps1'' by fully qualified path'}
|
||||
@{command = '.\[test1].ps1'; expectedCommand = '1.ps1'; expectedCommandCount =1; name = '''.\[test1].ps1'''}
|
||||
@{command = (Join-Path ${TestDrive} -ChildPath '[test1].ps1'); expectedCommand = '1.ps1'; expectedCommandCount =1 ; name = '''.\[test1].ps1'' by fully qualified path'}
|
||||
@{command = '.\[12].ps1'; expectedCommand = '1.ps1'; expectedCommandCount =0; name = 'relative path with bracket wildcard matctching multiple files'}
|
||||
@{command = (Join-Path ${TestDrive} -ChildPath '[12].ps1'); expectedCommand = '1.ps1'; expectedCommandCount =0 ; name = 'fully qualified path with bracket wildcard matctching multiple files'}
|
||||
)
|
||||
|
||||
Push-Location ${TestDrive}\
|
||||
|
@ -108,9 +197,12 @@ Describe "Command Discovery tests" -Tags "CI" {
|
|||
|
||||
It "Get-Command <name> should return <expectedCommandCount> command named '<expectedCommand>'" -TestCases $gcmWithWildcardCases {
|
||||
param($command, $expectedCommand, $expectedCommandCount)
|
||||
$commands = Get-Command -Name $command
|
||||
$commands = @(Get-Command -Name $command)
|
||||
$commands.Count | Should -Be $expectedCommandCount
|
||||
$commands.Name | Should -BeExactly $expectedCommand
|
||||
if($expectedCommandCount -gt 0)
|
||||
{
|
||||
$commands.Name | Should -BeExactly $expectedCommand
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue