Skip to content

Conversation

@vexx32
Copy link
Collaborator

@vexx32 vexx32 commented Oct 19, 2018

PR Summary

Fix #5717.
Fix #8076.

Test-Path is expected to be almost exclusively a True/False response cmdlet, and the cases where it may error or return an unexpected result are a few too many at present (see above issues). This PR allows $null / empty value(s) to be passed to Test-Path without throwing parameter binding exceptions. It also adds additional logic to Test-Path such that passing it $null, '', or ' ' (or any pure whitespace string) returns $false instead of throwing an error or returning $true on a "path" that is nothing but whitespace.

New behaviour:

PS> Test-Path ' '
False
PS> Test-Path ''
False
PS> Test-Path @()
False

Error conditions:

PS> Test-Path $null
PS> Test-Path $null, $null

These are non-terminating errors.

A small handful of tests predicated on Test-Path failing in these cases have been updated to simply transform the error into terminating via -ErrorAction, as it appears that the important part of the test was not Test-Path itself, but throwing a terminating error in specific code strictures.

Tests for the additional behaviours have also been added.

PR Checklist

CmdletProviderContext currentContext = CmdletProviderContext;

foreach (string path in _paths)
if (_paths != null && _paths.Length != 0)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually use short code path:

Suggested change
if (_paths != null && _paths.Length != 0)
if (_paths == null || _paths.Length == 0)
{
return false;
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is in the ProcessRecord() method, so returning false is not an option. I suppose WriteObject(false); return; is the most appropriate short-cut method here. Thank you!

@iSazonov
Copy link
Collaborator

@SteveL-MSFT Is this a breaking change?

@SteveL-MSFT SteveL-MSFT added Breaking-Change breaking change that may affect users Review - Committee The PR/Issue needs a review from the PowerShell Committee labels Oct 19, 2018
@vexx32 vexx32 changed the title Add empty / null path handling to Test-Path Test-Path: Return $false when given an empty or $null -Path/-LiteralPath value Oct 19, 2018
@SteveL-MSFT
Copy link
Member

@PowerShell/powershell-committee reviewed this, we believe the correct behavior is to accept null/nullcollection, but return non-terminating error at runtime (instead of exception or $false)

@SteveL-MSFT SteveL-MSFT added Committee-Reviewed PS-Committee has reviewed this and made a decision and removed Review - Committee The PR/Issue needs a review from the PowerShell Committee labels Oct 24, 2018
@iSazonov
Copy link
Collaborator

@vexx32 Please continue and open new issue in PowerShell-Docs.

$false will be output if it encounters a non-null but empty string.
@vexx32
Copy link
Collaborator Author

vexx32 commented Oct 25, 2018

Added non-terminating errors for the following cases, per PowerShell committee recommendation:

  1. Null collection handed to Test-Path (e.g., Test-Path $null)
  2. Collection of null items handed to Test-Path (e.g., Test-Path $null, $null)

$false will be output if:

  1. Path is invalid (same as existing behaviour)
  2. Path is empty or pure whitespace.

Docs issue: MicrosoftDocs/PowerShell-Docs#3165

@iSazonov
Copy link
Collaborator

@vexx32 Please add tests for new behaviors.


It 'Should write a non-terminating error when given a null path' {
{ Test-Path -Path $null -ErrorAction Stop } | Should -Throw -ErrorId 'NullPathNotPermitted'
{ Test-Path -Path $null -ErrorAction SilentlyContinue } | Should -Not -Throw
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems we should remove this. I am not sure that it tests "non-terminating"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I thought, too. Surprisingly, though, -ErrorAction SilentlyContinue will still trigger a | should -throw if and only if the error is terminating to start with (either throw or ThrowTerminatingError())

@indented-automation had to explain that one to me. :)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@adityapatwardhan @JamesWTruher @SteveL-MSFT Can we use the pattern to test non-terminating errors?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this change is explicitly to be a non-terminating error, this pattern seems fine. But should probably be followed up with -ErrorAction Stop to verify the non-terminating error is correct.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I follow? I don't see a need to double-check it with -ErrorAction Stop

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we need to add a comment to describe the pattern.

}

It 'Should write a non-terminating error when given a null path' {
{ Test-Path -Path $null -ErrorAction Stop } | Should -Throw -ErrorId 'NullPathNotPermitted'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we should tests here Test-Path $null, $null;.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Also had to modify some ErrorPosition tests as they were causing infinite loops when the cmldet simply wrote a non-terminating error.

@iSazonov
Copy link
Collaborator

@PowerShell/powershell-committee reviewed this, we believe the correct behavior is to accept null/nullcollection, but return non-terminating error at runtime (instead of exception or $false)

We'll get $false without -ErrorAction Stop - yes?

@vexx32
Copy link
Collaborator Author

vexx32 commented Oct 31, 2018

We'll get a non-terminating error per the recommendation. In terms of output this will effectively be treated as $null in expressions and statements; in most reasonable circumstances this will be coerced to$falsein a statement that is expecting a boolean conditional, yes.

Would explicitly outputting $false along with the error(s) be preferable?

@iSazonov
Copy link
Collaborator

iSazonov commented Nov 6, 2018

@vexx32 Could you please continue?

Update: Sorry, seems my comments was addressed.

@iSazonov
Copy link
Collaborator

@SteveL-MSFT @adityapatwardhan Could you please review the small PR?

@iSazonov iSazonov added the CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log label Nov 16, 2018
@vexx32
Copy link
Collaborator Author

vexx32 commented Nov 16, 2018

MacOS CI failure appears to be temporary and no related to the PR as far as I can see.

@iSazonov
Copy link
Collaborator

Restart MacOs CI.

@iSazonov iSazonov merged commit 775552c into PowerShell:master Nov 21, 2018
iSazonov pushed a commit to iSazonov/PowerShell that referenced this pull request Nov 29, 2018
@vexx32 vexx32 deleted the Test-Path/AllowNull branch January 22, 2019 13:19
@ZenoArrow
Copy link

Is this change due to be released in PowerShell 7? I haven't seen it in the release notes of the previews released to date, just wanted to check that it's still on course to be included.

@iSazonov
Copy link
Collaborator

iSazonov commented Nov 5, 2019

@ZenoArrow The fix is in current 7.0 Preview5 and will be in 7.0 GA too.
Also the fix was mentioned in CHANGELOG.md.

@ZenoArrow
Copy link

@iSazonov
Thanks for the heads up about the changelog. Referencing that file...
"Test-Path: Return $false when given an empty or $null -Path/-LiteralPath value (#8080) (Thanks @vexx32!)"

The behaviour seems to be fixed for empty path, but the behaviour is different from what I'd expect for $null values...

TestPath20191105

Am I missing something?

@vexx32
Copy link
Collaborator Author

vexx32 commented Nov 5, 2019

@ZenoArrow apologies! Looks like I neglected to update the PR description properly. The PS committee's decision was that we should maintain the error for $null values. I didn't agree, but that is that they decided and so that is how it ended up being implemented.

If you disagree with their decision there (I'm right there with you really) I'd recommend opening an issue so that they can see that the behaviour isn't as desired and we can have them reconvene and reassess. 🙂

/Cc @SteveL-MSFT

@ZenoArrow
Copy link

ZenoArrow commented Nov 6, 2019

@vexx32 Thanks for the heads up. I can raise a new issue, but it'd effectively just be a duplicate of the issue raised by @KevinMarquette last year...
#8076

Regarding returning the non-terminating error, I understand this to a degree, but what I can't understand is why Test-Path can't return more than just the error. Correct me if I'm wrong, but usually error stream output doesn't interfere with the standard output, so in my mind there's no reason not to return both (an error message to the error stream and a "False" value to the standard stream).

@vexx32
Copy link
Collaborator Author

vexx32 commented Nov 6, 2019

It certainly could! But it's not strictly necessary. If you're using Test-Path as a conditional in most cases the non-respose will be treated as $false.

For example:

if (Test-Path $null) { "true!" } Else { "false!" }

@ZenoArrow
Copy link

@vexx32 Good to know. In that case the current improvements to Test-Path should cover what I was hoping for.

@kilasuit kilasuit mentioned this pull request Oct 22, 2024
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Breaking-Change breaking change that may affect users CL-General Indicates that a PR should be marked as a general cmdlet change in the Change Log Committee-Reviewed PS-Committee has reviewed this and made a decision

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Test-Path -Path $null should return $false Test-Path returns $true for ' ' on Windows

4 participants