Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -34,30 +34,72 @@ Describe "Debug-Runspace" -Tag "CI" {

It "Should write attach event and mark runspace as having a remote debugger attached" {
$onAttachName = [System.Management.Automation.PSEngineEvent]::OnDebugAttach

$debugTarget = [PowerShell]::Create()
$null = $debugTarget.AddCommand('Wait-Event').AddParameter('SourceIdentifier', $onAttachName)
$waitTask = $debugTarget.BeginInvoke()

$debugTarget.Runspace.IsRemoteDebuggerAttached | Should -BeFalse

$debugger = [PowerShell]::Create()
$null = $debugger.AddCommand('Debug-Runspace').AddParameter('Id', $debugTarget.Runspace.Id)
$debugTask = $debugger.BeginInvoke()

$waitTask.AsyncWaitHandle.WaitOne(5000) | Should -BeTrue
$waitInfo = $debugTarget.EndInvoke($waitTask)
$waitInfo.SourceIdentifier | Should -Be $onAttachName

$debugTarget.Runspace.IsRemoteDebuggerAttached | Should -BeTrue

$debugger.Stop()
$exp = {
$debugger.EndInvoke($debugTask)
} | Should -Throw -PassThru
$exp.FullyQualifiedErrorId | Should -Be "PipelineStoppedException"

$debugTarget.Runspace.IsRemoteDebuggerAttached | Should -BeFalse

$targetRunspace = $null
$debugTarget = $null
$debugger = $null
$debugTask = $null

try {
# Open the target runspace up front so that 'Debug-Runspace' can never observe it in a
# non-Opened state, and so that its event manager is guaranteed to exist when the
# OnDebugAttach event is generated.
$targetRunspace = [runspacefactory]::CreateRunspace()
$targetRunspace.Open()

$debugTarget = [PowerShell]::Create()
$debugTarget.Runspace = $targetRunspace
$null = $debugTarget.AddCommand('Wait-Event').AddParameter('SourceIdentifier', $onAttachName)
$waitTask = $debugTarget.BeginInvoke()

# 'BeginInvoke' only queues the work. Wait until the 'Wait-Event' pipeline is actually
# running in the target runspace before attaching the debugger, so the attach event is
# never generated against a runspace that has not started executing the waiter.
$ready = Wait-UntilTrue -IntervalInMilliseconds 20 -TimeoutInMilliseconds 5000 -sb {
$debugTarget.InvocationStateInfo.State -eq [System.Management.Automation.PSInvocationState]::Running -and
$targetRunspace.RunspaceAvailability -eq [System.Management.Automation.Runspaces.RunspaceAvailability]::Busy
}
$ready | Should -BeTrue -Because "the 'Wait-Event' pipeline should be running in the target runspace"

$targetRunspace.IsRemoteDebuggerAttached | Should -BeFalse

$debugger = [PowerShell]::Create()
$null = $debugger.AddCommand('Debug-Runspace').AddParameter('Id', $targetRunspace.Id)
$debugTask = $debugger.BeginInvoke()

$waitTask.AsyncWaitHandle.WaitOne(5000) | Should -BeTrue
$waitInfo = $debugTarget.EndInvoke($waitTask)
$waitInfo.SourceIdentifier | Should -Be $onAttachName

$targetRunspace.IsRemoteDebuggerAttached | Should -BeTrue

$debugger.Stop()
$exp = {
$debugger.EndInvoke($debugTask)
} | Should -Throw -PassThru
$exp.FullyQualifiedErrorId | Should -Be "PipelineStoppedException"

# 'IsRemoteDebuggerAttached' is reset by the cmdlet as it unwinds, which happens
# asynchronously with respect to 'Stop' completing.
$detached = Wait-UntilTrue -IntervalInMilliseconds 20 -TimeoutInMilliseconds 5000 -sb {
-not $targetRunspace.IsRemoteDebuggerAttached
}
$detached | Should -BeTrue

$targetRunspace.IsRemoteDebuggerAttached | Should -BeFalse
}
finally {
if ($debugger) {
try { $debugger.Stop() } catch { Write-Warning "Failed to stop the debugger during cleanup: $_" }
$debugger.Dispose()
}

if ($debugTarget) {
try { $debugTarget.Stop() } catch { Write-Warning "Failed to stop the debug target during cleanup: $_" }
$debugTarget.Dispose()
}

if ($targetRunspace) { $targetRunspace.Dispose() }
}
}
}

Loading