PowerShell/test/powershell/Modules/Microsoft.PowerShell.Utility/RunspaceBreakpointManagement.Tests.ps1

207 lines
9.2 KiB
PowerShell

# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
$FeatureEnabled = $EnabledExperimentalFeatures.Contains('Microsoft.PowerShell.Utility.PSManageBreakpointsInRunspace')
Describe 'Runspace Breakpoint Unit Tests - Feature-Enabled' -Tags 'CI' {
BeforeAll {
if (!$FeatureEnabled) {
Write-Verbose 'Test series skipped. This series of tests requires the experimental feature ''Microsoft.PowerShell.Utility.PSManageBreakpointsInRunspace'' to be enabled.' -Verbose
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
$PSDefaultParameterValues['it:skip'] = $true
return
}
# Start a job; this will create a runspace in which we can manage breakpoints
$job = Start-Job -ScriptBlock {
Set-PSBreakpoint -Command Start-Sleep
1..240 | ForEach-Object {
Start-Sleep -Milliseconds 250
$_
Write-Error 'boo'
Write-Verbose 'Verbose' -Verbose
$DebugPreference = 'Continue'
Write-Debug 'Debug'
Write-Warning 'Warning'
}
}
# Wait for the child job that gets created to hit the breakpoint. This is the
# only safe way to know that the job has actually entered a running state and
# that the remote runspace is listening for requests.
Wait-UntilTrue { $job.ChildJobs.Count -gt 0 -and $job.ChildJobs[0].State -eq 'AtBreakpoint' } -TimeoutInMilliseconds 10000 -IntervalInMilliseconds 250
# Get the runspace for the running job
$jobRunspace = $job.ChildJobs[0].Runspace
}
AfterAll {
if (!$FeatureEnabled) {
$global:PSDefaultParameterValues = $originalDefaultParameterValues
return
}
# Remove the running job forcibly (whether it has finished or not)
Remove-Job -Job $job -Force
}
# Test the transformation attribute independently of PSBreakpoint cmdlet tests so that
# those tests can focus on scenarios expected to work.
Context 'Can transform a runspace name, id, or instanceid into a runspace' {
function Test-RunspaceTransform {
param(
[ValidateNotNull()]
[Runspace]
[System.Management.Automation.Runspaces.Runspace()]
$Runspace
)
$Runspace
}
It 'Transforms a valid runspace name into a runspace' {
Test-RunspaceTransform -Runspace $host.Runspace.Name | Should -Be $host.Runspace
}
It 'Transforms a valid runspace ID into a runspace' {
Test-RunspaceTransform -Runspace $host.Runspace.Id | Should -Be $host.Runspace
}
It 'Transforms a valid runspace instance ID into a runspace' {
Test-RunspaceTransform -Runspace $host.Runspace.InstanceId | Should -Be $host.Runspace
}
It 'Reports an argument transformation error when given invalid input' {
$e = { Test-RunspaceTransform -Runspace 'This is not a runspace name' } | Should -Throw -PassThru
$e.Exception.GetType().Name | Should -Be 'ParameterBindingArgumentTransformationException'
}
It 'Passes through $null without transforming it' {
$e = { Test-RunspaceTransform -Runspace $null } | Should -Throw -PassThru
$e.Exception.GetType().Name | Should -Be 'ParameterBindingValidationException'
}
}
Context 'Managing breakpoints in the host runspace' {
It 'Can set breakpoints' {
Set-PSBreakpoint -Command Test-ThisCommandDoesNotExist -Runspace $host.Runspace | Should -BeOfType [System.Management.Automation.CommandBreakpoint]
}
It 'Can get breakpoints, and the result breakpoints do not show the runspace id because they are local' {
foreach ($bp in Get-PSBreakpoint -Runspace $host.Runspace) {
Get-Member -InputObject $bp -Name RunspaceId -ErrorAction Ignore | Should -Be $null
}
}
It 'Can disable breakpoints in a pipeline' {
foreach ($bp in Get-PSBreakpoint -Runspace $host.Runspace | Disable-PSBreakpoint -PassThru) {
$bp.Enabled | Should -BeFalse
# This ensures we're working in the right runspace
Get-Member -InputObject $bp -Name RunspaceId -ErrorAction Ignore | Should -Be $null
}
}
It 'Can enable breakpoints in a pipeline' {
foreach ($bp in Get-PSBreakpoint -Runspace $host.Runspace | Enable-PSBreakpoint -PassThru) {
$bp.Enabled | Should -BeTrue
# This ensures we're working in the right runspace
Get-Member -InputObject $bp -Name RunspaceId -ErrorAction Ignore | Should -Be $null
}
}
It 'Can disable breakpoints by id' {
foreach ($bp in Get-PSBreakpoint -Runspace $host.Runspace) {
$bp = Disable-PSBreakpoint -Id $bp.Id -Runspace $host.Runspace -PassThru
$bp.Enabled | Should -BeFalse
# This ensures we're working in the right runspace
Get-Member -InputObject $bp -Name RunspaceId -ErrorAction Ignore | Should -Be $null
}
}
It 'Can enable breakpoints by id' {
foreach ($bp in Get-PSBreakpoint -Runspace $host.Runspace) {
$bp = Enable-PSBreakpoint -Id $bp.Id -Runspace $host.Runspace -PassThru
$bp.Enabled | Should -BeTrue
# This ensures we're working in the right runspace
Get-Member -InputObject $bp -Name RunspaceId -ErrorAction Ignore | Should -Be $null
}
}
It 'Can remove breakpoints' {
Get-PSBreakpoint -Runspace $host.Runspace | Remove-PSBreakpoint
Get-PSBreakpoint -Runspace $host.Runspace | Should -BeNull
}
}
Context 'Managing breakpoints in a remote runspace' {
AfterAll {
# Get rid of any breakpoints that were created in the default runspace.
# This is necessary due to a known bug that causes breakpoints with the
# same id to be created or updated in the default runspace.
Get-PSBreakpoint | Remove-PSBreakpoint
}
It 'Can set breakpoints' {
Set-PSBreakpoint -Command Write-Verbose -Action { break } -Runspace $jobRunspace | Should -BeOfType [System.Management.Automation.CommandBreakpoint]
Set-PSBreakpoint -Variable DebugPreference -Mode ReadWrite -Action { break } -Runspace $jobRunspace | Should -BeOfType [System.Management.Automation.VariableBreakpoint]
Set-PSBreakpoint -Script $PSCommandPath -Line 1 -Column 1 -Action { break } -Runspace $jobRunspace | Should -BeOfType [System.Management.Automation.LineBreakpoint]
}
It 'Can get breakpoints, and the result breakpoints show the remote runspace id' {
foreach ($bp in Get-PSBreakpoint -Runspace $jobRunspace) {
$bp.RunspaceId | Should -Be $jobRunspace.InstanceId
}
}
It 'Breakpoints are triggered by the remote debugger' {
$startTime = [DateTime]::UtcNow
$maxTimeToWait = [TimeSpan]'00:00:20'
while ($job.State -ne 'AtBreakpoint' -and ([DateTime]::UtcNow - $startTime) -lt $maxTimeToWait) {
Start-Sleep -Milliseconds 100 # Give the job a bit of time to hit a breakpoint
}
$job.State | Should -Be 'AtBreakpoint'
}
It 'Can disable breakpoints in a pipeline' {
foreach ($bp in Get-PSBreakpoint -Runspace $jobRunspace | Disable-PSBreakpoint -PassThru) {
$bp.Enabled | Should -BeFalse
# This ensures we're working in the right runspace
$bp.RunspaceId | Should -Be $jobRunspace.InstanceId
}
}
It 'Can enable breakpoints in a pipeline' {
foreach ($bp in Get-PSBreakpoint -Runspace $jobRunspace | Enable-PSBreakpoint -PassThru) {
$bp.Enabled | Should -BeTrue
# This ensures we're working in the right runspace
$bp.RunspaceId | Should -Be $jobRunspace.InstanceId
}
}
It 'Can disable breakpoints by id' {
foreach ($bp in Get-PSBreakpoint -Runspace $jobRunspace) {
$bp = Disable-PSBreakpoint -Id $bp.Id -Runspace $jobRunspace -PassThru
$bp.Enabled | Should -BeFalse
# This ensures we're working in the right runspace
$bp.RunspaceId | Should -Be $jobRunspace.InstanceId
}
}
It 'Can enable breakpoints in a pipeline' {
foreach ($bp in Get-PSBreakpoint -Runspace $jobRunspace) {
$bp = Enable-PSBreakpoint -Id $bp.Id -Runspace $jobRunspace -PassThru
$bp.Enabled | Should -BeTrue
# This ensures we're working in the right runspace
$bp.RunspaceId | Should -Be $jobRunspace.InstanceId
}
}
It 'Can remove breakpoints' {
Get-PSBreakpoint -Runspace $jobRunspace | Remove-PSBreakpoint
Get-PSBreakpoint -Runspace $jobRunspace | Should -BeNull
}
}
}