From a50070a68c9b9be059d298ab8530a130cd089be1 Mon Sep 17 00:00:00 2001 From: Raimund Andree Date: Fri, 9 Oct 2026 17:00:46 +0000 Subject: [PATCH] test: recognize the end of a pipeline by the type of the exception The Write wrappers of BaseCmdlet note what a later command raises, so PipelineControl.IsEnd is reached only for an exception that did not pass through one, and its branches had no test. The tests call it through reflection: for a PipelineStoppedException, for a stand-in type that derives from a class named FlowControlException, as PowerShell keeps the exceptions of break and continue internal, and for failures of an item, also one that has an inner exception that ends the pipeline. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: AI Assistant --- Tests/PipelineControl.Tests.ps1 | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/Tests/PipelineControl.Tests.ps1 b/Tests/PipelineControl.Tests.ps1 index 7507f8a..2c071ee 100644 --- a/Tests/PipelineControl.Tests.ps1 +++ b/Tests/PipelineControl.Tests.ps1 @@ -475,3 +475,37 @@ Describe 'A later command that ends the pipeline' { Assert-StreamStop -Name $Name -Stream $Stream -Style $Style } } + +# A cmdlet also meets the end of the pipeline where it did not write: another call of PowerShell can raise it too. The +# check that every catch makes recognizes the exceptions by their types. PowerShell keeps the exceptions of break and +# continue internal, so they are recognized by the name of their base type, which a stand-in with that name shows. +Describe 'Recognizing the end of a pipeline by the type of the exception' { + BeforeAll { + if (-not ('NtfsSecurityTests.StandInForBreak' -as [type])) { + # The compiler warns that the stand-in has the name of an imported type, and Add-Type treats a warning as an error. + Add-Type -IgnoreWarnings -TypeDefinition @' +namespace System.Management.Automation { public class FlowControlException : System.Exception { } } +namespace NtfsSecurityTests { public class StandInForBreak : System.Management.Automation.FlowControlException { } } +'@ + } + + $isEnd = [NTFSSecurity.BaseCmdlet].Assembly.GetType('NTFSSecurity.PipelineControl').GetMethod( + 'IsEnd', [System.Reflection.BindingFlags] 'NonPublic, Static') + } + + It 'Should recognize a PipelineStoppedException' { + $isEnd.Invoke($null, @([System.Management.Automation.PipelineStoppedException]::new())) | Should -BeTrue + } + + It 'Should recognize an exception whose base type is the flow control exception of PowerShell' { + $isEnd.Invoke($null, @([NtfsSecurityTests.StandInForBreak]::new())) | Should -BeTrue + } + + It 'Should not recognize ' -ForEach @( + @{ Description = 'a failure of an item'; Exception = [System.InvalidOperationException]::new('Failure') } + @{ Description = 'an access denial'; Exception = [System.UnauthorizedAccessException]::new('Denied') } + @{ Description = 'a failure with an inner exception that ends the pipeline'; Exception = [System.InvalidOperationException]::new('Failure', [System.Management.Automation.PipelineStoppedException]::new()) } + ) { + $isEnd.Invoke($null, @($Exception)) | Should -BeFalse + } +}