PowerShell/test/powershell/engine/Basic/CommandDiscovery.Tests.ps1

231 lines
11 KiB
PowerShell

# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
Describe "Command Discovery tests" -Tags "CI" {
BeforeAll {
Setup -f testscript.ps1 -Content "'This script should not run. Running from testscript.ps1'"
Setup -f testscripp.ps1 -Content "'This script should not run. Running from testscripp.ps1'"
$TestCasesCommandNotFound = @(
@{command = 'CommandThatDoesnotExist' ; testName = 'Non-existent command'}
@{command = 'testscrip?.ps1' ; testName = 'Multiple matches for filename'}
@{command = "demo" + [System.IO.Path]::DirectorySeparatorChar; testName = 'Non existent command with directory separator'}
@{command = [System.IO.Path]::DirectorySeparatorChar; testName = 'Directory separator'}
@{command = 'environment::\path'; testName = 'Provider qualified path'}
)
}
It "<testName>" -TestCases $TestCasesCommandNotFound {
param($command)
{ & $command } | Should -Throw -ErrorId 'CommandNotFoundException'
}
It "Command lookup with duplicate paths" {
$previousPath = $env:PSModulePath
try
{
New-Item -Path "$TestDrive\TestFunctionA" -ItemType Directory
New-Item -Path "$TestDrive\\TestFunctionA\TestFunctionA.psm1" -Value "function TestFunctionA {}" | Out-Null
$env:PSModulePath = "$TestDrive" + [System.IO.Path]::PathSeparator + "$TestDrive"
(Get-Command 'TestFunctionA').count | Should -Be 1
}
finally
{
$env:PSModulePath = $previousPath
}
}
It "Alias can be set for a cmdlet" {
Set-Alias 'AliasCommandDiscoveryTest' Get-ChildItem
$commands = (Get-Command 'AliasCommandDiscoveryTest')
$commands.Count | Should -Be 1
$aliasResult = $commands -as [System.Management.Automation.AliasInfo]
$aliasResult | Should -BeOfType System.Management.Automation.AliasInfo
$aliasResult.Name | Should -Be 'AliasCommandDiscoveryTest'
}
It "Cyclic aliases - direct" {
{
Set-Alias CyclicAliasA CyclicAliasB -Force
Set-Alias CyclicAliasB CyclicAliasA -Force
& CyclicAliasA
} | Should -Throw -ErrorId 'CommandNotFoundException'
}
It "Cyclic aliases - indirect" {
{
Set-Alias CyclicAliasA CyclicAliasB -Force
Set-Alias CyclicAliasB CyclicAliasC -Force
Set-Alias CyclicAliasC CyclicAliasA -Force
& CyclicAliasA
} | Should -Throw -ErrorId 'CommandNotFoundException'
}
It "Get-Command should return only CmdletInfo, FunctionInfo, AliasInfo or FilterInfo" {
$commands = Get-Command
$commands.Count | Should -BeGreaterThan 0
foreach($command in $commands)
{
$command.GetType().Name | Should -BeIn @("AliasInfo","FunctionInfo","CmdletInfo","FilterInfo")
}
}
It "Non-existent commands with wildcard should not write errors" {
Get-Command "CommandDoesNotExist*" -ErrorVariable ev -ErrorAction SilentlyContinue
$ev | Should -BeNullOrEmpty
}
It "Get- is prepended to commands" {
(& 'location').Path | Should -Be (Get-Location).Path
}
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}\
}
AfterAll {
Pop-Location
}
It "Get-Command <name> should return <expectedCommandCount> command named '<expectedCommand>'" -TestCases $gcmWithWildcardCases {
param($command, $expectedCommand, $expectedCommandCount)
$commands = @(Get-Command -Name $command)
$commands.Count | Should -Be $expectedCommandCount
if($expectedCommandCount -gt 0)
{
$commands.Name | Should -BeExactly $expectedCommand
}
}
}
Context "error cases" {
It 'Get-Command "less `"-PsPage %db?B of %DoesNotExist:`"" should return nothing' {
Get-Command -Name "less `"-PsPage %db?B of %DoesNotExist:`"" | Should -BeNullOrEmpty
}
It "Should return command not found for commands in the global scope" {
{Get-Command -Name 'global:help' -ErrorAction Stop} | Should -Throw -ErrorId 'CommandNotFoundException'
}
}
Context "Native command discovery" {
It 'Can discover a native command without extension' {
$expectedName = if ($IsWindows) { "ping.exe" } else { "ping" }
(Get-Command -Name "ping" -CommandType Application).Name | Should -Match $expectedName
}
It 'Can discover a native command with extension on Windows' -Skip:(-not $IsWindows) {
(Get-Command -Name "ping.exe" -CommandType Application).Name | Should -Match "ping.exe"
}
}
}