Add module to support Pester tests for automating debugger commands (stepInto, stepOut, etc.), along with basic tests (#9825)

This commit is contained in:
Kirk Munro 2019-06-13 11:19:13 -03:00 committed by Dongbo Wang
parent af1de9e88d
commit aac4c6ff21
5 changed files with 707 additions and 5 deletions

View file

@ -1237,9 +1237,8 @@ namespace System.Management.Automation
breakpoint.RemoveSelf(this);
if (_idToBreakpoint.Count == 0)
if (CanDisableDebugger)
{
// The last breakpoint was removed, turn off debugging.
SetInternalDebugMode(InternalDebugMode.Disabled);
}
@ -2099,6 +2098,19 @@ namespace System.Management.Automation
}
}
private bool CanDisableDebugger
{
get
{
// The debugger can be disbled if there are no breakpoints
// left and if we are not currently stepping in the debugger.
return _idToBreakpoint.Count == 0 &&
_currentDebuggerAction != DebuggerResumeAction.StepInto &&
_currentDebuggerAction != DebuggerResumeAction.StepOver &&
_currentDebuggerAction != DebuggerResumeAction.StepOut;
}
}
private static bool IsSystemLockedDown
{
get
@ -3857,9 +3869,8 @@ namespace System.Management.Automation
_context.IgnoreScriptDebug = _savedIgnoreScriptDebug;
_context.PSDebugTraceLevel = 0;
_context.PSDebugTraceStep = false;
if (!_idToBreakpoint.Any())
if (CanDisableDebugger)
{
// Only disable debug mode if there are no breakpoints.
SetInternalDebugMode(InternalDebugMode.Disabled);
}
}

View file

@ -109,7 +109,7 @@ namespace System.Management.Automation
fileContents: script);
internal static ScriptBlock CreateDelayParsedScriptBlock(string script, bool isProductCode)
=> new ScriptBlock(new CompiledScriptBlockData(script, isProductCode));
=> new ScriptBlock(new CompiledScriptBlockData(script, isProductCode)) { DebuggerHidden = true };
/// <summary>
/// Returns a new scriptblock bound to a module. Any local variables in the

View file

@ -0,0 +1,442 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
Describe 'Basic debugger command tests' -tag 'CI' {
BeforeAll {
Register-DebuggerHandler
}
AfterAll {
Unregister-DebuggerHandler
}
Context 'Help (?, h) command should display the debugger help message' {
BeforeAll {
$testScript = {
try {
$bp = Set-PSBreakpoint -Command Get-Process
Get-Process -Id $PID > $null
} finally {
Remove-PSBreakPoint -Breakpoint $bp
}
}
$results = @(Test-Debugger -ScriptBlock $testScript -CommandQueue '?','h')
$result = @{
'?' = if ($results.Count -gt 0) {$results[0].Output -join [Environment]::NewLine}
'h' = if ($results.Count -gt 1) {$results[1].Output -join [Environment]::NewLine}
}
}
It 'Should show 3 debugger commands were invoked' {
# One extra for the implicit 'c' command that keeps the debugger automation moving
$results.Count | Should -Be 3
}
It '''h'' and ''?'' should show identical help messages' {
$result['?'] | Should -BeExactly $result['h']
}
It 'Should only have non-empty string output from the help command' {
$results[0].Output | Should -BeOfType string
$result['?'] | Should -Match '\S'
}
It 'Should show help for stepInto' {$result['?'] | Should -Match '\ss, stepInto\s+'}
It 'Should show help for stepOver' {$result['?'] | Should -Match '\sv, stepOver\s+'}
It 'Should show help for stepOut' {$result['?'] | Should -Match '\so, stepOut\s+'}
It 'Should show help for continue' {$result['?'] | Should -Match '\sc, continue\s+'}
It 'Should show help for quit' {$result['?'] | Should -Match '\sq, quit\s+'}
It 'Should show help for detach' {$result['?'] | Should -Match '\sd, detach\s+'}
It 'Should show help for Get-PSCallStack' {$result['?'] | Should -Match '\sk, Get-PSCallStack\s+'}
It 'Should show help for list' {$result['?'] | Should -Match '\sl, list\s+'}
It 'Should show help for <enter>' {$result['?'] | Should -Match '\s<enter>\s+'}
It 'Should show help for help' {$result['?'] | Should -Match '\s\?, h\s+'}
}
Context 'List (l, list) command should show the script and the current position' {
BeforeAll {
$testScript = {
try {
$bp = Set-PSBreakpoint -Command Get-Process
Get-Process -Id $PID > $null
} finally {
Remove-PSBreakPoint -Breakpoint $bp
}
}
$testScriptList = @'
1:
2: try {
3: $bp = Set-PSBreakpoint -Command Get-Process
4:* Get-Process -Id $PID > $null
5: } finally {
6: Remove-PSBreakPoint -Breakpoint $bp
7: }
8:
'@
$results = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 'l','list')
$result = @{
'l' = if ($results.Count -gt 0) {$results[0].Output -replace '\s+$' -join [Environment]::NewLine -replace "^[`r`n]+|[`r`n]+$"}
'list' = if ($results.Count -gt 1) {$results[1].Output -replace '\s+$' -join [Environment]::NewLine -replace "^[`r`n]+|[`r`n]+$"}
}
}
It 'Should show 3 debugger commands were invoked' {
# One extra for the implicit 'c' command that keeps the debugger automation moving
$results.Count | Should -Be 3
}
It '''l'' and ''list'' should show identical script listings' {
$result['l'] | Should -BeExactly $result['list']
}
It 'Should only have non-empty string output from the list command' {
$results[0].Output | Should -BeOfType string
$result['l'] | Should -Match '\S'
}
It 'Should show the entire script listing with the current position on line 5' {
$result['l'] | Should -BeExactly $testScriptList
}
}
Context 'List (l, list) command should support a start position' {
BeforeAll {
$testScript = {
try {
$bp = Set-PSBreakpoint -Command Get-Process
Get-Process -Id $PID > $null
} finally {
Remove-PSBreakPoint -Breakpoint $bp
}
}
$testScriptList = @'
4:* Get-Process -Id $PID > $null
5: } finally {
6: Remove-PSBreakPoint -Breakpoint $bp
7: }
8:
'@
$results = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 'l 4','list 4')
$result = @{
'l 4' = if ($results.Count -gt 0) {$results[0].Output -replace '\s+$' -join [Environment]::NewLine -replace "^[`r`n]+|[`r`n]+$"}
'list 4' = if ($results.Count -gt 1) {$results[1].Output -replace '\s+$' -join [Environment]::NewLine -replace "^[`r`n]+|[`r`n]+$"}
}
}
It 'Should show 3 debugger commands were invoked' {
# One extra for the implicit 'c' command that keeps the debugger automation moving
$results.Count | Should -Be 3
}
It '''l 4'' and ''list 4'' should show identical script listings' {
$result['l 4'] | Should -BeExactly $result['list 4']
}
It 'Should only have non-empty string output from the list command' {
$results[0].Output | Should -BeOfType string
$result['l 4'] | Should -Match '\S'
}
It 'Should show a partial script listing starting on line 4 with the current position on line 5' {
$result['l 4'] | Should -BeExactly $testScriptList
}
}
Context 'List (l, list) command should support a start position and a line count' {
BeforeAll {
$testScript = {
try {
$bp = Set-PSBreakpoint -Command Get-Process
Get-Process -Id $PID > $null
} finally {
Remove-PSBreakPoint -Breakpoint $bp
}
}
$testScriptList = @'
3: $bp = Set-PSBreakpoint -Command Get-Process
4:* Get-Process -Id $PID > $null
'@
$results = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 'l 3 2','list 3 2')
$result = @{
'l 3 2' = if ($results.Count -gt 0) {$results[0].Output -replace '\s+$' -join [Environment]::NewLine -replace "^[`r`n]+|[`r`n]+$"}
'list 3 2' = if ($results.Count -gt 1) {$results[1].Output -replace '\s+$' -join [Environment]::NewLine -replace "^[`r`n]+|[`r`n]+$"}
}
}
It 'Should show 3 debugger commands were invoked' {
# One extra for the implicit 'c' command that keeps the debugger automation moving
$results.Count | Should -Be 3
}
It '''l 3 2'' and ''list 3 2'' should show identical script listings' {
$result['l 3 2'] | Should -BeExactly $result['list 3 2']
}
It 'Should only have non-empty string output from the list command' {
$results[0].Output | Should -BeOfType string
$result['l 3 2'] | Should -Match '\S'
}
It 'Should show a partial script listing showing 3 lines starting on line 4 with the current position on line 5' {
$result['l 3 2'] | Should -BeExactly $testScriptList
}
}
Context 'Callstack (k, Get-PSCallStack) command should show the current call stack' {
BeforeAll {
$testScript = {
try {
$bp = Set-PSBreakpoint -Command Get-Process
Get-Process -Id $PID > $null
} finally {
Remove-PSBreakPoint -Breakpoint $bp
}
}
$results = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 'k','Get-PSCallStack')
$result = @{
'k' = if ($results.Count -gt 0) {$results[0].Output}
'Get-PSCallStack' = if ($results.Count -gt 1) {$results[1].Output}
}
}
It 'Should show 3 debugger commands were invoked' {
# One extra for the implicit 'c' command that keeps the debugger automation moving
$results.Count | Should -Be 3
}
It 'Should only have CallStackFrame output from the callstack command' {
$results[0].Output | Should -BeOfType System.Management.Automation.CallStackFrame
}
It '''k'' and ''Get-PSCallStack'' should show identical script listings' {
[string[]]$result['k'] -join [Environment]::NewLine | Should -BeExactly ([string[]]$result['Get-PSCallStack'] -join [Environment]::NewLine)
}
}
}
Describe 'Simple debugger stepping command tests' -tag 'CI' {
BeforeAll {
Register-DebuggerHandler
}
AfterAll {
Unregister-DebuggerHandler
}
Context 'StepInto steps into the current command if possible; otherwise it steps over the command' {
BeforeAll {
$testScript = {
try {
$bp = Set-PSBreakpoint -Command ForEach-Object
Get-Process -Id $PID | ForEach-Object {
'One fish, two fish'
'Red fish, blue fish'
} *> $null
} finally {
Remove-PSBreakPoint -Breakpoint $bp
}
}
$result = @{
's' = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 's','s','s','s')
'stepInto' = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 'stepInto','stepInto','stepInto','stepInto')
}
}
It 'Should show 4 debugger commands were invoked twice' {
# One extra for the implicit 'c' command that keeps the debugger automation moving
$result['s'].Count | Should -Be 5
$result['stepInto'].Count | Should -Be 5
}
It '''s'' and ''stepInto'' should have identical behaviour' {
for ($i = 0; $i -lt 3; $i++) {
$result['s'][$i] | ShouldHaveSameExtentAs -DebuggerCommandResult $result['stepInto'][$i]
}
}
It 'The first extent should be the statement containing ForEach-Object' {
$result['s'][0] | ShouldHaveExtent -FromLine 4 -FromColumn 21 -ToLine 7 -ToColumn 31
}
It 'The second extent should be in the nested scriptblock' {
$result['s'][1] | ShouldHaveExtent -Line 4 -FromColumn 59 -ToColumn 60
}
It 'The third extent should be on ''One fish, two fish''' {
$result['s'][2] | ShouldHaveExtent -Line 5 -FromColumn 25 -ToColumn 45
}
It 'The fourth extent should be on ''Red fish, blue fish''' {
$result['s'][3] | ShouldHaveExtent -Line 6 -FromColumn 25 -ToColumn 46
}
}
Context 'StepOver steps over the current command, unless it contains a triggerable breakpoint' {
BeforeAll {
$testScript = {
try {
$bp1 = Set-PSBreakpoint -Command ForEach-Object
$bp2 = Set-PSBreakpoint -Command ConvertTo-Csv | Disable-PSBreakpoint -PassThru
Get-Process -Id $PID | ForEach-Object -Process {
$_ | ConvertTo-Csv
} *> $null
Enable-PSBreakpoint -Breakpoint $bp2
& {
Get-Date | ConvertTo-Csv
} *> $null
} finally {
Remove-PSBreakPoint -Breakpoint $bp1,$bp2
}
}
$result = @{
'v' = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 'v','v','v','v')
'stepOver' = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 'stepOver','stepOver','stepOver','stepOver')
}
}
It 'Should show 4 debugger commands were invoked twice' {
# One extra for the implicit 'c' command that keeps the debugger automation moving
$result['v'].Count | Should -Be 5
$result['stepOver'].Count | Should -Be 5
}
It '''v'' and ''stepOver'' should have identical behaviour' {
for ($i = 0; $i -lt 3; $i++) {
$result['v'][$i] | ShouldHaveSameExtentAs -DebuggerCommandResult $result['stepOver'][$i]
}
}
It 'The first extent should be the statement containing ForEach-Object' {
$result['v'][0] | ShouldHaveExtent -FromLine 5 -FromColumn 21 -ToLine 7 -ToColumn 31
}
It 'The second extent should be on Enable-PSBreakpoint' {
$result['v'][1] | ShouldHaveExtent -Line 8 -FromColumn 21 -ToColumn 57
}
It 'The third extent should be on the script block invoked with the call operator' {
$result['v'][2] | ShouldHaveExtent -FromLine 9 -FromColumn 21 -ToLine 11 -ToColumn 31
}
It 'The fourth extent should be on the ConvertTo-Csv breakpoint inside the script block' {
$result['v'][3] | ShouldHaveExtent -Line 10 -FromColumn 25 -ToColumn 49
}
}
Context 'StepOut steps out of the current command, unless it contains a triggerable breakpoint after the current location' {
BeforeAll {
$testScript = {
try {
$bps = Set-PSBreakpoint -Command Get-Process,ConvertTo-Csv
& {
$process = Get-Process -Id $PID
$process.Id
}
$date = Get-Date
$date | ConvertTo-Csv
} finally {
Remove-PSBreakPoint -Breakpoint $bps
}
}
$result = @{
'o' = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 'o','o','o')
'stepOut' = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 'stepOut','stepOut','stepOut')
}
}
It 'Should show 3 debugger commands were invoked twice' {
# One extra for the implicit 'c' command that keeps the debugger automation moving
$result['o'].Count | Should -Be 4
$result['stepOut'].Count | Should -Be 4
}
It '''o'' and ''stepOut'' should have identical behaviour' {
for ($i = 0; $i -lt 3; $i++) {
$result['o'][$i] | ShouldHaveSameExtentAs -DebuggerCommandResult $result['stepOut'][$i]
}
}
It 'The first extent should be on Get-Process' {
$result['o'][0] | ShouldHaveExtent -Line 5 -FromColumn 25 -ToColumn 56
}
It 'The second extent should be on Get-Date' {
$result['o'][1] | ShouldHaveExtent -Line 8 -FromColumn 21 -ToColumn 37
}
It 'The third extent should be on the ConvertTo-Csv breakpoint' {
$result['o'][2] | ShouldHaveExtent -Line 9 -FromColumn 21 -ToColumn 42
}
}
}
Describe 'Debugger bug fix tests' -tag 'CI' {
BeforeAll {
Register-DebuggerHandler
}
AfterAll {
Unregister-DebuggerHandler
}
Context 'Stepping works beyond Remove-PSBreakpoint (Issue #9824)' {
BeforeAll {
$testScript = {
function Test-Issue9824 {
$bp = Set-PSBreakpoint -Command Remove-PSBreakpoint
Remove-PSBreakPoint -Breakpoint $bp
}
Test-Issue9824
1 + 1
}
$result = @{
's' = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 's','s','s')
'v' = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 'v','v','v')
'o' = @(Test-Debugger -ScriptBlock $testScript -CommandQueue 'o','o')
}
}
It 'Should show 3 debugger commands were invoked for stepInto' {
# One extra for the implicit 'c' command that keeps the debugger automation moving
$result['s'].Count | Should -Be 4
}
It 'Should show 3 debugger commands were invoked for stepOver' {
# One extra for the implicit 'c' command that keeps the debugger automation moving
$result['v'].Count | Should -Be 4
}
It 'Should show 2 debugger commands were invoked for stepOut' {
# One extra for the implicit 'c' command that keeps the debugger automation moving
$result['o'].Count | Should -Be 3
}
It 'The last extent for stepInto should be on 1 + 1' {
$result['s'][2] | ShouldHaveExtent -Line 7 -FromColumn 17 -ToColumn 22
}
It 'The last extent for stepOver should be on 1 + 1' {
$result['v'][2] | ShouldHaveExtent -Line 7 -FromColumn 17 -ToColumn 22
}
It 'The last extent for stepOut should be on 1 + 1' {
$result['o'][1] | ShouldHaveExtent -Line 7 -FromColumn 17 -ToColumn 22
}
}
}

View file

@ -0,0 +1,30 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
@{
RootModule = 'HelpersDebugger.psm1'
ModuleVersion = '1.0'
GUID = '37a454d7-8acd-40e6-8a2c-43c9d46b1b0c'
CompanyName = 'Microsoft Corporation'
Copyright = 'Copyright (c) Microsoft Corporation. All rights reserved.'
Description = 'Helper module for Pester tests that automate the debugger'
PowerShellVersion = '5.0'
FunctionsToExport = @(
'Register-DebuggerHandler'
'ShouldHaveExtent'
'ShouldHaveSameExtentAs'
'Test-Debugger'
'Unregister-DebuggerHandler'
)
CmdletsToExport = @()
AliasesToExport = @()
}

View file

@ -0,0 +1,219 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
# Ensure that terminating errors terminate when importing the module.
trap {throw $_}
# Strict mode FTW.
Set-StrictMode -Version Latest
# Enable explicit export so that there are no surprises with commands exported from the module.
Export-ModuleMember
# Grab the internal ScriptPosition property once and re-use it in the ps1xml file
$internalExtentProperty = [System.Management.Automation.InvocationInfo].GetProperty('ScriptPosition', [System.Reflection.BindingFlags]'NonPublic,Instance')
# A debugger handler that can be used to automatically control the debugger
$debuggerStopHandler = {
param($s, $e)
# If we're not handling a debugger stop event during the execution of
# Test-Debugger, then simply continue execution
if (@(Get-Variable -Name dbgCmdQueue,dbgResults -Scope Script -ErrorAction Ignore).Count -ne 2) {
$e.ResumeAction = [System.Management.Automation.DebuggerResumeAction]::Continue
return
}
do {
if ($script:dbgCmdQueue.Count -eq 0) {
# If there are no more commands to process, continue execution
$stringDbgCommand = 'c'
} else {
$stringDbgCommand = $script:dbgCmdQueue.Dequeue()
}
$dbgCmd = [System.Management.Automation.PSCommand]::new()
$dbgCmd.AddCommand($stringDbgCommand)
$output = [System.Management.Automation.PSDataCollection[PSObject]]::new()
$result = $Host.Runspace.Debugger.ProcessCommand($dbgCmd, $output)
$script:dbgResults += [pscustomobject]@{
PSTypeName = 'DebuggerCommandResult'
Command = $stringDbgCommand
Context = $PSDebugContext
Output = $output
}
} while ($result -eq $null -or $result.ResumeAction -eq $null)
$e.ResumeAction = $result.ResumeAction
}
# A flag to identify if the debugger handler has been added or not
$debuggerStopHandlerRegistered = $false
function Register-DebuggerHandler {
[CmdletBinding()]
[OutputType([System.Void])]
param()
try {
$callerEAP = $ErrorActionPreference
# We disable debugger interactivity so that all debugger events go through
# the DebuggerStop event only (i.e. breakpoints don't actually generate a
# prompt for user interaction)
$host.DebuggerEnabled = $false
$host.Runspace.Debugger.add_DebuggerStop($script:debuggerStopHandler)
$script:debuggerStopHandlerRegistered = $true
} catch {
Write-Error -ErrorRecord $_ -ErrorAction $callerEAP
}
}
Export-ModuleMember -Function Register-DebuggerHandler
function Unregister-DebuggerHandler {
[CmdletBinding()]
[OutputType([System.Void])]
param()
try {
$callerEAP = $ErrorActionPreference
$host.Runspace.Debugger.remove_DebuggerStop($script:debuggerStopHandler)
$host.DebuggerEnabled = $true
$script:debuggerStopHandlerRegistered = $false
} catch {
Write-Error -ErrorRecord $_ -ErrorAction $callerEAP
}
}
Export-ModuleMember -Function Unregister-DebuggerHandler
function Test-Debugger {
[CmdletBinding()]
[OutputType('DebuggerCommandResult')]
param(
[Parameter(Position=0, Mandatory)]
[ValidateNotNullOrEmpty()]
[Alias('sb')]
[ScriptBlock]
$ScriptBlock,
[Parameter()]
[ValidateNotNullOrEmpty()]
[string[]]
$CommandQueue
)
try {
$callerEAP = $ErrorActionPreference
# If the debugger is not set up properly, notify the user with an error message
if (-not $script:debuggerStopHandlerRegistered -or $host.DebuggerEnabled) {
$message = 'You must invoke Register-DebuggerHandler before invoking Test-Debugger, and Unregister-DebuggerHandler after invoking Test-Debugger. As a best practice, invoke Register-DebuggerHandler in the BeforeAll block and Unregister-DebuggerHandler in the AfterAll block of your test script.'
$exception = [System.InvalidOperationException]::new($message)
$errorRecord = [System.Management.Automation.ErrorRecord]::new($exception, $exception.GetType().Name, 'InvalidOperation', $null)
throw $errorRecord
}
$script:dbgResults = @()
$script:dbgCmdQueue = [System.Collections.Queue]::new()
foreach ($command in $CommandQueue) {
$script:dbgCmdQueue.Enqueue($command)
}
# We re-create the script block before invoking it to ensure that it will
# work regardless of where the script itself was defined in the test file.
# We also silence any standard output because this invocation is about the
# debugger output, not the output of the script itself.
& {
[System.Diagnostics.DebuggerStepThrough()]
[CmdletBinding()]
param()
try {
$ErrorActionPreference = [System.Management.Automation.ActionPreference]::Stop
[ScriptBlock]::Create($ScriptBlock).Invoke() > $null
} catch {
Write-Error -ErrorRecord $_ -ErrorAction Stop
}
}
$script:dbgResults
} catch {
Write-Error -ErrorRecord $_ -ErrorAction $callerEAP
} finally {
Remove-Variable -Name dbgResults -Scope Script -ErrorAction Ignore
Remove-Variable -Name dbgCmdQueue -Scope Script -ErrorAction Ignore
}
}
Export-ModuleMember -Function Test-Debugger
function Get-DebuggerExtent {
[CmdletBinding()]
param(
[Parameter(Position=0, Mandatory, ValueFromPipeline)]
[ValidateNotNull()]
[PSTypeName('DebuggerCommandResult')]
$DebuggerCommandResult
)
process {
try {
$callerEAP = $ErrorActionPreference
$script:internalExtentProperty.GetValue($DebuggerCommandResult.Context.InvocationInfo)
} catch {
Write-Error -ErrorRecord $_ -ErrorAction $callerEAP
}
}
}
function ShouldHaveExtent {
[CmdletBinding(DefaultParameterSetName='SingleLineExtent')]
param(
[Parameter(Position=0, Mandatory, ValueFromPipeline)]
[ValidateNotNull()]
[PSTypeName('DebuggerCommandResult')]
$DebuggerCommandResult,
[Parameter(Mandatory, ParameterSetName='SingleLineExtent')]
[ValidateRange(1, [int]::MaxValue)]
[int]
$Line,
[Parameter(Mandatory, ParameterSetName='MultilineExtent')]
[ValidateRange(1, [int]::MaxValue)]
[int]
$FromLine,
[Parameter(Mandatory)]
[ValidateRange(1, [int]::MaxValue)]
[int]
$FromColumn,
[Parameter(Mandatory, ParameterSetName='MultilineExtent')]
[ValidateRange(1, [int]::MaxValue)]
[int]
$ToLine,
[Parameter(Mandatory)]
[ValidateRange(1, [int]::MaxValue)]
[int]
$ToColumn
)
process {
$extent = Get-DebuggerExtent -DebuggerCommandResult $DebuggerCommandResult
$extent.StartLineNumber | Should -Be $(if ($PSCmdlet.ParameterSetName -eq 'SingleLineExtent') {$Line} else {$FromLine})
$extent.StartColumnNumber | Should -Be $FromColumn
$extent.EndLineNumber | Should -Be $(if ($PSCmdlet.ParameterSetName -eq 'SingleLineExtent') {$Line} else {$ToLine})
$extent.EndColumnNumber | Should -Be $ToColumn
}
}
Export-ModuleMember -Function ShouldHaveExtent
function ShouldHaveSameExtentAs {
[CmdletBinding()]
param(
[Parameter(Position=0, Mandatory, ValueFromPipeline)]
[ValidateNotNull()]
[PSTypeName('DebuggerCommandResult')]
$SourceDebuggerCommandResult,
[Parameter(Position=1, Mandatory)]
[ValidateNotNull()]
[Alias('DebuggerCommandResult')]
[PSTypeName('DebuggerCommandResult')]
$TargetDebuggerCommandResult
)
begin {
$targetExtent = Get-DebuggerExtent -DebuggerCommandResult $TargetDebuggerCommandResult
}
process {
$sourceExtent = Get-DebuggerExtent -DebuggerCommandResult $SourceDebuggerCommandResult
$sourceExtent | Should -Be $targetExtent
}
}
Export-ModuleMember -Function ShouldHaveSameExtentAs