diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/PingPathCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/PingPathCommand.cs index 3d50ea95737..e76695a9047 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/PingPathCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/PingPathCommand.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +using System; using System.Management.Automation; using Dbg = System.Management.Automation; @@ -41,6 +42,9 @@ public class TestPathCommand : CoreCommandWithCredentialsBase /// [Parameter(Position = 0, ParameterSetName = "Path", Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] + [AllowNull] + [AllowEmptyCollection] + [AllowEmptyString] public string[] Path { get { return _paths; } @@ -53,6 +57,9 @@ public string[] Path [Parameter(ParameterSetName = "LiteralPath", Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] [Alias("PSPath", "LP")] + [AllowNull] + [AllowEmptyCollection] + [AllowEmptyString] public string[] LiteralPath { get { return _paths; } @@ -124,7 +131,7 @@ internal override object GetDynamicParameters(CmdletProviderContext context) if (this.PathType == TestPathType.Any && !IsValid) { - if (Path != null && Path.Length > 0) + if (Path != null && Path.Length > 0 && Path[0] != null) { result = InvokeProvider.Item.ItemExistsDynamicParameters(Path[0], context); } @@ -133,6 +140,7 @@ internal override object GetDynamicParameters(CmdletProviderContext context) result = InvokeProvider.Item.ItemExistsDynamicParameters(".", context); } } + return result; } // GetDynamicParameters @@ -154,12 +162,39 @@ internal override object GetDynamicParameters(CmdletProviderContext context) /// protected override void ProcessRecord() { + if (_paths == null || _paths.Length == 0) + { + WriteError(new ErrorRecord( + new ArgumentNullException(TestPathResources.PathIsNullOrEmptyCollection), + "NullPathNotPermitted", + ErrorCategory.InvalidArgument, + Path)); + + return; + } + CmdletProviderContext currentContext = CmdletProviderContext; foreach (string path in _paths) { bool result = false; + if (path == null) + { + WriteError(new ErrorRecord( + new ArgumentNullException(TestPathResources.PathIsNullOrEmptyCollection), + "NullPathNotPermitted", + ErrorCategory.InvalidArgument, + Path)); + continue; + } + + if (string.IsNullOrWhiteSpace(path)) + { + WriteObject(result); + continue; + } + try { if (IsValid) @@ -184,6 +219,7 @@ protected override void ProcessRecord() } } } + // Any of the known exceptions means the path does not exist. catch (PSNotSupportedException) { diff --git a/src/Microsoft.PowerShell.Commands.Management/resources/TestPathResources.resx b/src/Microsoft.PowerShell.Commands.Management/resources/TestPathResources.resx new file mode 100644 index 00000000000..c60c4318a64 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Management/resources/TestPathResources.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The provided Path argument was null or an empty collection. + + diff --git a/test/powershell/Language/Scripting/ErrorPosition.Tests.ps1 b/test/powershell/Language/Scripting/ErrorPosition.Tests.ps1 index 62777bda32e..1853e9cba46 100644 --- a/test/powershell/Language/Scripting/ErrorPosition.Tests.ps1 +++ b/test/powershell/Language/Scripting/ErrorPosition.Tests.ps1 @@ -17,7 +17,7 @@ for ($null[0]; '@ $for_pipeline_initializer_script = @' $test = 1 -for (Test-Path $null; +for (Test-Path $null -ErrorAction Stop; $test -gt 1;) { } '@ $for_expression_condition_script = @' @@ -27,7 +27,7 @@ for (;$null[0];) '@ $for_pipeline_condition_script = @' $test = 1 -for (;Test-Path $null;) +for (;Test-Path $null -ErrorAction Stop;) { } '@ $do_while_expression_condition_script = @' @@ -38,7 +38,7 @@ while ($null[0]) $do_while_pipeline_condition_script = @' $test = 1 do {} -while (Test-Path $null) +while (Test-Path $null -ErrorAction Stop) '@ $do_until_expression_condition_script = @' $test = 1 @@ -48,7 +48,7 @@ until ($null[0]) $do_until_pipeline_condition_script = @' $test = 1 do {} -until (Test-Path $null) +until (Test-Path $null -ErrorAction Stop) '@ $testCases = @( diff --git a/test/powershell/Modules/Microsoft.PowerShell.Management/Test-Path.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Management/Test-Path.Tests.ps1 index 694a7ae8606..48a80d8e0b8 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Management/Test-Path.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Management/Test-Path.Tests.ps1 @@ -2,123 +2,143 @@ # Licensed under the MIT License. Describe "Test-Path" -Tags "CI" { BeforeAll { - $testdirectory = $TestDrive - $testfilename = New-Item -path $testdirectory -Name testfile.txt -ItemType file -Value 1 -force + $testdirectory = $TestDrive + $testfilename = New-Item -path $testdirectory -Name testfile.txt -ItemType file -Value 1 -force - # populate with additional files - New-Item -Path $testdirectory -Name datestfile -value 1 -ItemType file | Out-Null - New-Item -Path $testdirectory -Name gatestfile -value 1 -ItemType file | Out-Null - New-Item -Path $testdirectory -Name usr -value 1 -ItemType directory | Out-Null + # populate with additional files + New-Item -Path $testdirectory -Name datestfile -value 1 -ItemType file | Out-Null + New-Item -Path $testdirectory -Name gatestfile -value 1 -ItemType file | Out-Null + New-Item -Path $testdirectory -Name usr -value 1 -ItemType directory | Out-Null - $nonExistentDir = Join-Path -Path (Join-Path -Path $testdirectory -ChildPath usr) -ChildPath bin - $nonExistentPath = Join-Path -Path (Join-Path -Path (Join-Path -Path $testdirectory -ChildPath usr) -ChildPath bin) -ChildPath error + $nonExistentDir = Join-Path -Path (Join-Path -Path $testdirectory -ChildPath usr) -ChildPath bin + $nonExistentPath = Join-Path -Path (Join-Path -Path (Join-Path -Path $testdirectory -ChildPath usr) -ChildPath bin) -ChildPath error } It "Should be called on an existing path without error" { - { Test-Path $testdirectory } | Should -Not -Throw - { Test-Path -Path $testdirectory } | Should -Not -Throw - { Test-Path -LiteralPath $testdirectory } | Should -Not -Throw + { Test-Path $testdirectory } | Should -Not -Throw + { Test-Path -Path $testdirectory } | Should -Not -Throw + { Test-Path -LiteralPath $testdirectory } | Should -Not -Throw } It "Should allow piping objects to it" { - { $testdirectory | Test-Path } | Should -Not -Throw + { $testdirectory | Test-Path } | Should -Not -Throw - $testdirectory | Test-Path | Should -BeTrue - $nonExistentDir | Test-Path | Should -BeFalse + $testdirectory | Test-Path | Should -BeTrue + $nonExistentDir | Test-Path | Should -BeFalse } It "Should be called on a nonexistent path without error" { - { Test-Path -Path $nonExistentPath } | Should -Not -Throw + { Test-Path -Path $nonExistentPath } | Should -Not -Throw } It "Should return false for a nonexistent path" { - Test-Path -Path $nonExistentPath | Should -BeFalse + Test-Path -Path $nonExistentPath | Should -BeFalse } It "Should return true for an existing path" { - { Test-Path -Path $testdirectory } | Should -BeTrue + { Test-Path -Path $testdirectory } | Should -BeTrue + } + + It 'Should return false for an empty string' { + Test-Path -Path '' | Should -BeFalse + } + + It 'Should return false for a whitespace string' { + Test-Path -Path ' ' | Should -BeFalse + } + + It 'Should write a non-terminating error when given a null path' { + # This ensures the error is non-terminating; a terminating error would fail the first test + { Test-Path -Path $null -ErrorAction SilentlyContinue } | Should -Not -Throw + { Test-Path -Path $null -ErrorAction Stop } | Should -Throw -ErrorId 'NullPathNotPermitted,Microsoft.PowerShell.Commands.TestPathCommand' + } + + It 'Should write a non-terminating error when given an array of null paths' { + # This ensures the error is non-terminating; a terminating error would fail the first test + { Test-Path -Path $null, $null -ErrorAction SilentlyContinue } | Should -Not -Throw + { Test-Path -Path $null, $null -ErrorAction Stop } | Should -Throw -ErrorId 'NullPathNotPermitted,Microsoft.PowerShell.Commands.TestPathCommand' } It "Should be able to accept a regular expression" { - { Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u*") } | Should -Not -Throw - { Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u[a-z]r") } | Should -Not -Throw + { Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u*") } | Should -Not -Throw + { Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u[a-z]r") } | Should -Not -Throw } It "Should be able to return the correct result when a regular expression is used" { - Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u*") | Should -BeTrue - Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u[a-z]*") | Should -BeTrue + Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u*") | Should -BeTrue + Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u[a-z]*") | Should -BeTrue - Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "aoeu*") | Should -BeFalse - Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u[A-Z]") | Should -BeFalse + Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "aoeu*") | Should -BeFalse + Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "u[A-Z]") | Should -BeFalse } It "Should return false when the Leaf pathtype is used on a directory" { - Test-Path -Path $testdirectory -PathType Leaf | Should -BeFalse + Test-Path -Path $testdirectory -PathType Leaf | Should -BeFalse } It "Should return true when the Leaf pathtype is used on an existing endpoint" { - Test-Path -Path $testfilename -PathType Leaf | Should -BeTrue + Test-Path -Path $testfilename -PathType Leaf | Should -BeTrue } It "Should return false when the Leaf pathtype is used on a nonexistent file" { - Test-Path -Path "aoeu" -PathType Leaf | Should -BeFalse + Test-Path -Path "aoeu" -PathType Leaf | Should -BeFalse } It "Should return true when the Leaf pathtype is used on a file using the Type alias instead of PathType" { - Test-Path -Path $testfilename -Type Leaf | Should -BeTrue + Test-Path -Path $testfilename -Type Leaf | Should -BeTrue } It "Should be able to search multiple regular expressions using the include switch" { - { Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "*") -Include t* } | Should -BeTrue + { Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "*") -Include t* } | Should -BeTrue } It "Should be able to exclude a regular expression using the exclude switch" { - { Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "*") -Exclude v* } | Should -BeTrue + { Test-Path -Path (Join-Path -Path $testdirectory -ChildPath "*") -Exclude v* } | Should -BeTrue } It "Should be able to exclude multiple regular expressions using the exclude switch" { - # tests whether there's any files in the usr directory that don't start with 'd' or 'g' - { Test-Path -Path $testfilename -Exclude d*, g* } | Should -BeTrue + # tests whether there's any files in the usr directory that don't start with 'd' or 'g' + { Test-Path -Path $testfilename -Exclude d*, g* } | Should -BeTrue } It "Should return true if the syntax of the path is correct when using the IsValid switch" { - { Test-Path -Path $nonExistentPath -IsValid } | Should -BeTrue + { Test-Path -Path $nonExistentPath -IsValid } | Should -BeTrue } It "Should return false if the syntax of the path is incorrect when using the IsValid switch" { - $badPath = " :;!@#$%^&*(){}?+|_-" - Test-Path -Path $badPath -IsValid | Should -BeFalse + $badPath = " :;!@#$%^&*(){}?+|_-" + Test-Path -Path $badPath -IsValid | Should -BeFalse } It "Should return true on paths containing spaces when the path is surrounded in quotes" { - Test-Path -Path "/totally a valid/path" -IsValid | Should -BeTrue + Test-Path -Path "/totally a valid/path" -IsValid | Should -BeTrue } It "Should throw on paths containing spaces when the path is not surrounded in quotes" { - { Test-Path -Path /a path/without quotes/around/it -IsValid } | Should -Throw -ErrorId "PositionalParameterNotFound,Microsoft.PowerShell.Commands.TestPathCommand" + { Test-Path -Path /a path/without quotes/around/it -IsValid } | Should -Throw -ErrorId "PositionalParameterNotFound,Microsoft.PowerShell.Commands.TestPathCommand" } It "Should return true if a directory leads or trails with a space when surrounded by quotes" { - Test-Path -Path "/a path / with/funkyspaces" -IsValid | Should -BeTrue + Test-Path -Path "/a path / with/funkyspaces" -IsValid | Should -BeTrue } It "Should return true on a valid path when the LiteralPath switch is used" { - Test-Path -LiteralPath $testfilename | Should -BeTrue + Test-Path -LiteralPath $testfilename | Should -BeTrue } It "Should return false if regular expressions are used with the LiteralPath switch" { - Test-Path -LiteralPath (Join-Path -Path $testdirectory -ChildPath "u*") | Should -BeFalse - Test-Path -LiteralPath (Join-Path -Path $testdirectory -ChildPath "u[a-z]r") | Should -BeFalse + Test-Path -LiteralPath (Join-Path -Path $testdirectory -ChildPath "u*") | Should -BeFalse + Test-Path -LiteralPath (Join-Path -Path $testdirectory -ChildPath "u[a-z]r") | Should -BeFalse } It "Should return false if regular expressions are used with the LiteralPath alias PSPath switch" { - Test-Path -PSPath (Join-Path -Path $testdirectory -ChildPath "u*") | Should -BeFalse - Test-Path -PSPath (Join-Path -Path $testdirectory -ChildPath "u[a-z]r") | Should -BeFalse + Test-Path -PSPath (Join-Path -Path $testdirectory -ChildPath "u*") | Should -BeFalse + Test-Path -PSPath (Join-Path -Path $testdirectory -ChildPath "u[a-z]r") | Should -BeFalse } It "Should return true if used on components other than filesystem objects" { - Test-Path Alias:\gci | Should -BeTrue - Test-Path Env:\PATH | Should -BeTrue + Test-Path Alias:\gci | Should -BeTrue + Test-Path Env:\PATH | Should -BeTrue } }