Fix sequence point update for switch/if/for/while/do-while/do-until statements (#7305)

- Make switch-statement report correct error position when it fails to evaluate the condition.
- Make for-statement report correct error position when it fails to evaluate the initializer.
- For the condition of `if/for/while/do-while/do-until` statements, the sequence point update is either duplicate in some cases (for `if/for/while`) which causes debugger to stop at the condition twice  before moving forward, or missing (for `do-while/do-until`) which causes debugger to skip the condition. They are fixed.
This commit is contained in:
Dongbo Wang
2018-07-25 09:56:32 -07:00
committed by GitHub
parent 439f97c533
commit 87d8fc969c
3 changed files with 461 additions and 44 deletions
@@ -1,6 +1,10 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
$script1 = @'
Describe "Breakpoints when set should be hit" -tag "CI" {
Context "Basic tests" {
BeforeAll {
$script = @'
'aaa'.ToString() > $null
'aa' > $null
"a" 2> $null | ForEach-Object { $_ }
@@ -8,32 +12,285 @@ $script1 = @'
'bb'.ToSTring() > $null
'bbb'
'@
$script2 = @'
"line 1"
"line 2"
"line 3"
$path = Setup -PassThru -File BasicTest.ps1 -Content $script
$bps = 1..6 | ForEach-Object { set-psbreakpoint -script $path -line $_ -Action { continue } }
}
AfterAll {
$bps | Remove-PSBreakPoint
}
It "A redirected breakpoint is hit" {
& $path
foreach ( $bp in $bps ) {
$bp.HitCount | Should -Be 1
}
}
}
Context "Break point on switch condition should be hit only when enumerating it" {
BeforeAll {
$script = @'
$test = 1..2
switch ($test)
{
default {}
}
'@
$path = Setup -PassThru -File SwitchScript.ps1 -Content $script
$breakpoint = Set-PSBreakpoint -Script $path -Line 2 -Action { continue }
}
AfterAll {
Remove-PSBreakpoint -Breakpoint $breakpoint
}
It "switch condition should be hit 3 times" {
## MoveNext() will be called on the condition for 3 times
$null = & $path
$breakpoint.HitCount | Should -Be 3
}
}
Context "Break point on for-statement initializer should be hit" {
BeforeAll {
$for_script_1 = @'
$test = 2
for ("string".Length;
$test -gt 0; $test--) { }
'@
Describe "Breakpoints when set should be hit" -tag "CI" {
BeforeAll {
$path = setup -pass -f TestScript_1.ps1 -content $script1
$bps = 1..6 | ForEach-Object { set-psbreakpoint -script $path -line $_ -Action { continue } }
$for_script_2 = @'
$test = $PSCommandPath
for (Test-Path $test;
$test -eq "blah";) { }
'@
$for_script_3 = @'
for ($test = 2;
$test -gt 0; $test--) { }
'@
$for_script_4 = @'
$test = 2
for (;$test -gt 0;
$test--) { }
'@
$for_script_5 = @'
$test = $PSCommandPath
for (;Test-Path $test;)
{
$test = "blah"
}
'@
$ForScript_1 = Setup -PassThru -File ForScript_1.ps1 -Content $for_script_1
$bp_1 = Set-PSBreakpoint -Script $ForScript_1 -Line 2 -Action { continue }
$ForScript_2 = Setup -PassThru -File ForScript_2.ps1 -Content $for_script_2
$bp_2 = Set-PSBreakpoint -Script $ForScript_2 -Line 2 -Action { continue }
$ForScript_3 = Setup -PassThru -File ForScript_3.ps1 -Content $for_script_3
$bp_3 = Set-PSBreakpoint -Script $ForScript_3 -Line 1 -Action { continue }
$ForScript_4 = Setup -PassThru -File ForScript_4.ps1 -Content $for_script_4
$bp_4 = Set-PSBreakpoint -Script $ForScript_4 -Line 2 -Action { continue }
$ForScript_5 = Setup -PassThru -File ForScript_5.ps1 -Content $for_script_5
$bp_5 = Set-PSBreakpoint -Script $ForScript_5 -Line 2 -Action { continue }
$testCases = @(
@{ Name = "expression initializer should be hit once"; Path = $ForScript_1; Breakpoint = $bp_1; HitCount = 1 }
@{ Name = "pipeline initializer should be hit once"; Path = $ForScript_2; Breakpoint = $bp_2; HitCount = 1 }
@{ Name = "assignment initializer should be hit 3 times"; Path = $ForScript_3; Breakpoint = $bp_3; HitCount = 1 }
@{ Name = "pipeline condition should be hit 3 times"; Path = $ForScript_4; Breakpoint = $bp_4; HitCount = 3 }
@{ Name = "pipeline condition should be hit 2 times"; Path = $ForScript_5; Breakpoint = $bp_5; HitCount = 2 }
)
}
AfterAll {
Get-PSBreakpoint -Script $ForScript_1, $ForScript_2, $ForScript_3, $ForScript_4, $ForScript_5 | Remove-PSBreakpoint
}
It "for-statement <Name>" -TestCases $testCases {
param($Path, $Breakpoint, $HitCount)
$null = & $Path
$Breakpoint.HitCount | Should -Be $HitCount
}
}
AfterAll {
$bps | Remove-PSBreakPoint
Context "Break point on while loop condition should be hit" {
BeforeAll {
$while_script_1 = @'
$test = "string"
while ($test.Contains("str"))
{
$test = "blah"
}
'@
$while_script_2 = @'
$test = $PSCommandPath
while (Test-Path $test)
{
$test = "blah"
}
'@
$WhileScript_1 = Setup -PassThru -File WhileScript_1.ps1 -Content $while_script_1
$bp_1 = Set-PSBreakpoint -Script $WhileScript_1 -Line 2 -Action { continue }
$WhileScript_2 = Setup -PassThru -File WhileScript_2.ps1 -Content $while_script_2
$bp_2 = Set-PSBreakpoint -Script $WhileScript_2 -Line 2 -Action { continue }
$testCases = @(
@{ Name = "expression condition should be hit 2 times"; Path = $WhileScript_1; Breakpoint = $bp_1; HitCount = 2 }
@{ Name = "pipeline condition should be hit 2 times"; Path = $WhileScript_2; Breakpoint = $bp_2; HitCount = 2 }
)
}
AfterAll {
Get-PSBreakpoint -Script $WhileScript_1, $WhileScript_2 | Remove-PSBreakpoint
}
It "while loop <Name>" -TestCases $testCases {
param($Path, $Breakpoint, $HitCount)
$null = & $Path
$Breakpoint.HitCount | Should -Be $HitCount
}
}
It "A redirected breakpoint is hit" {
& $path
foreach ( $bp in $bps ) {
$bp.HitCount | Should -Be 1
Context "Break point on do-while loop condition should be hit" {
BeforeAll {
$do_while_script_1 = @'
$test = "blah"
do { echo $test }
while ($test.Contains("str"))
'@
$do_while_script_2 = @'
$test = "blah"
do { echo $test }
while (Test-Path $test)
'@
$DoWhileScript_1 = Setup -PassThru -File DoWhileScript_1.ps1 -Content $do_while_script_1
$bp_1 = Set-PSBreakpoint -Script $DoWhileScript_1 -Line 2 -Action { continue }
$DoWhileScript_2 = Setup -PassThru -File DoWhileScript_2.ps1 -Content $do_while_script_2
$bp_2 = Set-PSBreakpoint -Script $DoWhileScript_2 -Line 2 -Action { continue }
$testCases = @(
@{ Name = "expression condition should be hit 2 times"; Path = $DoWhileScript_1; Breakpoint = $bp_1; HitCount = 1 }
@{ Name = "pipeline condition should be hit 2 times"; Path = $DoWhileScript_2; Breakpoint = $bp_2; HitCount = 1 }
)
}
AfterAll {
Get-PSBreakpoint -Script $DoWhileScript_1, $DoWhileScript_2 | Remove-PSBreakpoint
}
It "Do-While loop <Name>" -TestCases $testCases {
param($Path, $Breakpoint, $HitCount)
$null = & $Path
$Breakpoint.HitCount | Should -Be $HitCount
}
}
Context "Break point on do-until loop condition should be hit" {
BeforeAll {
$do_until_script_1 = @'
$test = "blah"
do { echo $test }
until ($test.Contains("bl"))
'@
$do_until_script_2 = @'
$test = $PSCommandPath
do { echo $test }
until (Test-Path $test)
'@
$DoUntilScript_1 = Setup -PassThru -File DoUntilScript_1.ps1 -Content $do_until_script_1
$bp_1 = Set-PSBreakpoint -Script $DoUntilScript_1 -Line 2 -Action { continue }
$DoUntilScript_2 = Setup -PassThru -File DoUntilScript_2.ps1 -Content $do_until_script_2
$bp_2 = Set-PSBreakpoint -Script $DoUntilScript_2 -Line 2 -Action { continue }
$testCases = @(
@{ Name = "expression condition should be hit 2 times"; Path = $DoUntilScript_1; Breakpoint = $bp_1; HitCount = 1 }
@{ Name = "pipeline condition should be hit 2 times"; Path = $DoUntilScript_2; Breakpoint = $bp_2; HitCount = 1 }
)
}
AfterAll {
Get-PSBreakpoint -Script $DoUntilScript_1, $DoUntilScript_2 | Remove-PSBreakpoint
}
It "Do-Until loop <Name>" -TestCases $testCases {
param($Path, $Breakpoint, $HitCount)
$null = & $Path
$Breakpoint.HitCount | Should -Be $HitCount
}
}
Context "Break point on if condition should be hit" {
BeforeAll {
$if_script_1 = @'
if ("string".Contains('str'))
{ }
'@
$if_script_2 = @'
if (Test-Path $PSCommandPath)
{ }
'@
$if_script_3 = @'
if ($false) {}
elseif ("string".Contains('str'))
{ }
'@
$if_script_4 = @'
if ($false) {}
elseif (Test-Path $PSCommandPath)
{ }
'@
$IfScript_1 = Setup -PassThru -File IfScript_1.ps1 -Content $if_script_1
$bp_1 = Set-PSBreakpoint -Script $IfScript_1 -Line 1 -Action { continue }
$IfScript_2 = Setup -PassThru -File IfScript_2.ps1 -Content $if_script_2
$bp_2 = Set-PSBreakpoint -Script $IfScript_2 -Line 1 -Action { continue }
$IfScript_3 = Setup -PassThru -File IfScript_3.ps1 -Content $if_script_3
$bp_3 = Set-PSBreakpoint -Script $IfScript_3 -Line 2 -Action { continue }
$IfScript_4 = Setup -PassThru -File IfScript_4.ps1 -Content $if_script_4
$bp_4 = Set-PSBreakpoint -Script $IfScript_4 -Line 2 -Action { continue }
$testCases = @(
@{ Name = "expression if-condition should be hit once"; Path = $IfScript_1; Breakpoint = $bp_1; HitCount = 1 }
@{ Name = "pipeline if-condition should be hit once"; Path = $IfScript_2; Breakpoint = $bp_2; HitCount = 1 }
@{ Name = "expression elseif-condition should be hit once"; Path = $IfScript_3; Breakpoint = $bp_3; HitCount = 1 }
@{ Name = "pipeline elseif-condition should be hit once"; Path = $IfScript_4; Breakpoint = $bp_4; HitCount = 1 }
)
}
AfterAll {
Get-PSBreakpoint -Script $IfScript_1, $IfScript_2, $IfScript_3, $IfScript_4 | Remove-PSBreakpoint
}
It "If statement <Name>" -TestCases $testCases {
param($Path, $Breakpoint, $HitCount)
$null = & $Path
$Breakpoint.HitCount | Should -Be $HitCount
}
}
}
Describe "It should be possible to reset runspace debugging" -tag "Feature" {
BeforeAll {
$path = setup -pass -f TestScript_2.ps1 -content $script2
$scriptPath = "$testdrive/TestScript_2.ps1"
$script = @'
"line 1"
"line 2"
"line 3"
'@
$scriptPath = Setup -PassThru -File TestScript.ps1 -Content $script
$iss = [initialsessionstate]::CreateDefault2();
$rs = [runspacefactory]::CreateRunspace($iss)
$rs.Name = "TestRunspaceDebuggerReset"
@@ -87,6 +344,6 @@ Describe "It should be possible to reset runspace debugging" -tag "Feature" {
}
It "The script should run without a break" {
$ps.Commands.Clear()
$ps.addscript("$testdrive/TestScript_2.ps1").Invoke().Count | Should -Be 3
$ps.addscript($scriptPath).Invoke().Count | Should -Be 3
}
}