Skip to content

Commit a7a289d

Browse files
committed
Fix DebugServiceTests
By ensuring that our event queue is only accessed from the threadpool, not the pipeline thread. We now do this in the event handler and the assertions, instead of around calls to the `debugService`.
1 parent 6ef2d0b commit a7a289d

File tree

1 file changed

+51
-53
lines changed

1 file changed

+51
-53
lines changed

test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs

Lines changed: 51 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -80,20 +80,20 @@ public async Task InitializeAsync()
8080
public async Task DisposeAsync()
8181
{
8282
debugService.Abort();
83-
debuggerStoppedQueue.Dispose();
8483
await Task.Run(psesHost.StopAsync);
84+
debuggerStoppedQueue.Dispose();
8585
}
8686

8787
/// <summary>
88-
/// This event handler lets us test that the debugger stopped or paused as expected. It will
89-
/// deadlock if called in the PSES Pipeline Thread, which can easily happen in this test
90-
/// code when methods on <see cref="debugService" /> are called. Hence we treat this test
91-
/// code like UI code and use 'ConfigureAwait(true)' or 'Task.Run(...)' to ensure we stay
92-
/// OFF the pipeline thread.
88+
/// This event handler lets us test that the debugger stopped or paused
89+
/// as expected. It will deadlock if called in the PSES Pipeline Thread.
90+
/// Hence we use 'Task.Run(...)' when accessing the queue to ensure we
91+
/// stay OFF the pipeline thread.
9392
/// </summary>
9493
/// <param name="sender"></param>
9594
/// <param name="e"></param>
96-
private void OnDebuggerStopped(object sender, DebuggerStoppedEventArgs e) => debuggerStoppedQueue.Add(e);
95+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "VSTHRD110:Observe result of async calls", Justification = "This intentionally fires and forgets on another thread.")]
96+
private void OnDebuggerStopped(object sender, DebuggerStoppedEventArgs e) => Task.Run(() => debuggerStoppedQueue.Add(e));
9797

9898
private ScriptFile GetDebugScript(string fileName) => workspace.GetFile(TestUtilities.GetSharedPath(Path.Combine("Debugging", fileName)));
9999

@@ -116,20 +116,20 @@ private Task ExecuteScriptFileAsync(string scriptFilePath, params string[] args)
116116

117117
private Task ExecuteVariableScriptFileAsync() => ExecuteScriptFileAsync(variableScriptFile.FilePath);
118118

119-
private void AssertDebuggerPaused()
119+
private async Task AssertDebuggerPaused()
120120
{
121121
using CancellationTokenSource cts = new(60000);
122-
DebuggerStoppedEventArgs eventArgs = debuggerStoppedQueue.Take(cts.Token);
122+
DebuggerStoppedEventArgs eventArgs = await Task.Run(() => debuggerStoppedQueue.Take(cts.Token));
123123
Assert.Empty(eventArgs.OriginalEvent.Breakpoints);
124124
}
125125

126-
private void AssertDebuggerStopped(
126+
private async Task AssertDebuggerStopped(
127127
string scriptPath = "",
128128
int lineNumber = -1,
129129
CommandBreakpointDetails commandBreakpointDetails = default)
130130
{
131-
using CancellationTokenSource cts = new(60000);
132-
DebuggerStoppedEventArgs eventArgs = debuggerStoppedQueue.Take(cts.Token);
131+
using CancellationTokenSource cts = new(30000);
132+
DebuggerStoppedEventArgs eventArgs = await Task.Run(() => debuggerStoppedQueue.Take(cts.Token));
133133

134134
Assert.True(psesHost.DebugContext.IsStopped);
135135

@@ -174,8 +174,8 @@ await debugService.SetCommandBreakpointsAsync(
174174
Task<IReadOnlyList<int>> executeTask = psesHost.ExecutePSCommandAsync<int>(
175175
new PSCommand().AddScript("Get-Random -SetSeed 42 -Maximum 100"), CancellationToken.None);
176176

177-
AssertDebuggerStopped("", 1);
178-
await Task.Run(debugService.Continue);
177+
await AssertDebuggerStopped("", 1);
178+
debugService.Continue();
179179
Assert.Equal(17, (await executeTask)[0]);
180180

181181
StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync();
@@ -216,7 +216,7 @@ public async Task DebuggerAcceptsScriptArgs(string[] args)
216216

217217
Task _ = ExecuteScriptFileAsync(oddPathScriptFile.FilePath, args);
218218

219-
AssertDebuggerStopped(oddPathScriptFile.FilePath, 3);
219+
await AssertDebuggerStopped(oddPathScriptFile.FilePath, 3);
220220

221221
VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName);
222222

@@ -283,7 +283,7 @@ public async Task DebuggerStopsOnFunctionBreakpoints()
283283
new[] { CommandBreakpointDetails.Create("Write-Host") });
284284

285285
Task _ = ExecuteDebugFileAsync();
286-
AssertDebuggerStopped(debugScriptFile.FilePath, 6);
286+
await AssertDebuggerStopped(debugScriptFile.FilePath, 6);
287287

288288
VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName);
289289

@@ -294,8 +294,8 @@ public async Task DebuggerStopsOnFunctionBreakpoints()
294294
Assert.Equal("1", i.ValueString);
295295

296296
// The function breakpoint should fire the next time through the loop.
297-
await Task.Run(debugService.Continue);
298-
AssertDebuggerStopped(debugScriptFile.FilePath, 6);
297+
debugService.Continue();
298+
await AssertDebuggerStopped(debugScriptFile.FilePath, 6);
299299

300300
variables = await GetVariables(VariableContainerDetails.LocalScopeName);
301301

@@ -350,9 +350,9 @@ await debugService.SetLineBreakpointsAsync(
350350
});
351351

352352
Task _ = ExecuteDebugFileAsync();
353-
AssertDebuggerStopped(debugScriptFile.FilePath, 5);
354-
await Task.Run(debugService.Continue);
355-
AssertDebuggerStopped(debugScriptFile.FilePath, 7);
353+
await AssertDebuggerStopped(debugScriptFile.FilePath, 5);
354+
debugService.Continue();
355+
await AssertDebuggerStopped(debugScriptFile.FilePath, 7);
356356
}
357357

358358
[Fact]
@@ -368,7 +368,7 @@ await debugService.SetLineBreakpointsAsync(
368368
});
369369

370370
Task _ = ExecuteDebugFileAsync();
371-
AssertDebuggerStopped(debugScriptFile.FilePath, 7);
371+
await AssertDebuggerStopped(debugScriptFile.FilePath, 7);
372372

373373
VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName);
374374

@@ -380,8 +380,8 @@ await debugService.SetLineBreakpointsAsync(
380380

381381
// The conditional breakpoint should not fire again, until the value of
382382
// i reaches breakpointValue2.
383-
await Task.Run(debugService.Continue);
384-
AssertDebuggerStopped(debugScriptFile.FilePath, 7);
383+
debugService.Continue();
384+
await AssertDebuggerStopped(debugScriptFile.FilePath, 7);
385385

386386
variables = await GetVariables(VariableContainerDetails.LocalScopeName);
387387

@@ -404,7 +404,7 @@ await debugService.SetLineBreakpointsAsync(
404404
});
405405

406406
Task _ = ExecuteDebugFileAsync();
407-
AssertDebuggerStopped(debugScriptFile.FilePath, 6);
407+
await AssertDebuggerStopped(debugScriptFile.FilePath, 6);
408408

409409
VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName);
410410

@@ -425,7 +425,7 @@ await debugService.SetLineBreakpointsAsync(
425425
new[] { BreakpointDetails.Create(debugScriptFile.FilePath, 6, null, "$i % 2 -eq 0", $"{hitCount}") });
426426

427427
Task _ = ExecuteDebugFileAsync();
428-
AssertDebuggerStopped(debugScriptFile.FilePath, 6);
428+
await AssertDebuggerStopped(debugScriptFile.FilePath, 6);
429429

430430
VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName);
431431

@@ -493,18 +493,16 @@ public async Task DebuggerBreaksWhenRequested()
493493
IReadOnlyList<LineBreakpoint> confirmedBreakpoints = await GetConfirmedBreakpoints(debugScriptFile);
494494
Assert.Empty(confirmedBreakpoints);
495495
Task _ = ExecuteDebugFileAsync();
496-
// NOTE: This must be run on a separate thread so the async event handlers can fire.
497-
await Task.Run(debugService.Break);
498-
AssertDebuggerPaused();
496+
debugService.Break();
497+
await AssertDebuggerPaused();
499498
}
500499

501500
[Fact]
502501
public async Task DebuggerRunsCommandsWhileStopped()
503502
{
504503
Task _ = ExecuteDebugFileAsync();
505-
// NOTE: This must be run on a separate thread so the async event handlers can fire.
506-
await Task.Run(debugService.Break);
507-
AssertDebuggerPaused();
504+
debugService.Break();
505+
await AssertDebuggerPaused();
508506

509507
// Try running a command from outside the pipeline thread
510508
Task<IReadOnlyList<int>> executeTask = psesHost.ExecutePSCommandAsync<int>(
@@ -524,7 +522,7 @@ await debugService.SetCommandBreakpointsAsync(
524522

525523
ScriptFile testScript = GetDebugScript("PSDebugContextTest.ps1");
526524
Task _ = ExecuteScriptFileAsync(testScript.FilePath);
527-
AssertDebuggerStopped(testScript.FilePath, 11);
525+
await AssertDebuggerStopped(testScript.FilePath, 11);
528526

529527
VariableDetails prompt = await debugService.EvaluateExpressionAsync("prompt", false, CancellationToken.None);
530528
Assert.Equal("True > ", prompt.ValueString);
@@ -549,7 +547,7 @@ await debugService.SetCommandBreakpointsAsync(
549547
NullLoggerFactory.Instance, null, debugService, null, null, psesHost, workspace, null, psesHost);
550548

551549
Task _ = configurationDoneHandler.LaunchScriptAsync(scriptPath);
552-
AssertDebuggerStopped(scriptPath, 1);
550+
await AssertDebuggerStopped(scriptPath, 1);
553551

554552
VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.CommandVariablesName);
555553
VariableDetailsBase myInvocation = Array.Find(variables, v => v.Name == "$MyInvocation");
@@ -627,7 +625,7 @@ await debugService.SetLineBreakpointsAsync(
627625
new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 8) });
628626

629627
Task _ = ExecuteVariableScriptFileAsync();
630-
AssertDebuggerStopped(variableScriptFile.FilePath);
628+
await AssertDebuggerStopped(variableScriptFile.FilePath);
631629

632630
VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName);
633631

@@ -645,7 +643,7 @@ await debugService.SetLineBreakpointsAsync(
645643
new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 21) });
646644

647645
Task _ = ExecuteVariableScriptFileAsync();
648-
AssertDebuggerStopped(variableScriptFile.FilePath);
646+
await AssertDebuggerStopped(variableScriptFile.FilePath);
649647

650648
VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName);
651649

@@ -695,7 +693,7 @@ await debugService.SetLineBreakpointsAsync(
695693
new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 14) });
696694

697695
Task _ = ExecuteVariableScriptFileAsync();
698-
AssertDebuggerStopped(variableScriptFile.FilePath);
696+
await AssertDebuggerStopped(variableScriptFile.FilePath);
699697

700698
VariableScope[] scopes = debugService.GetVariableScopes(0);
701699
VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName);
@@ -721,8 +719,8 @@ await debugService.SetLineBreakpointsAsync(
721719

722720
// The above just tests that the debug service returns the correct new value string.
723721
// Let's step the debugger and make sure the values got set to the new values.
724-
await Task.Run(debugService.StepOver);
725-
AssertDebuggerStopped(variableScriptFile.FilePath);
722+
debugService.StepOver();
723+
await AssertDebuggerStopped(variableScriptFile.FilePath);
726724

727725
// Test set of a local string variable (not strongly typed)
728726
variables = await GetVariables(VariableContainerDetails.LocalScopeName);
@@ -749,7 +747,7 @@ await debugService.SetLineBreakpointsAsync(
749747

750748
// Execute the script and wait for the breakpoint to be hit
751749
Task _ = ExecuteVariableScriptFileAsync();
752-
AssertDebuggerStopped(variableScriptFile.FilePath);
750+
await AssertDebuggerStopped(variableScriptFile.FilePath);
753751

754752
VariableScope[] scopes = debugService.GetVariableScopes(0);
755753
VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName);
@@ -777,8 +775,8 @@ await debugService.SetLineBreakpointsAsync(
777775

778776
// The above just tests that the debug service returns the correct new value string.
779777
// Let's step the debugger and make sure the values got set to the new values.
780-
await Task.Run(debugService.StepOver);
781-
AssertDebuggerStopped(variableScriptFile.FilePath);
778+
debugService.StepOver();
779+
await AssertDebuggerStopped(variableScriptFile.FilePath);
782780

783781
// Test set of a local string variable (not strongly typed but force conversion)
784782
variables = await GetVariables(VariableContainerDetails.LocalScopeName);
@@ -805,7 +803,7 @@ await debugService.SetLineBreakpointsAsync(
805803

806804
// Execute the script and wait for the breakpoint to be hit
807805
Task _ = ExecuteVariableScriptFileAsync();
808-
AssertDebuggerStopped(variableScriptFile.FilePath);
806+
await AssertDebuggerStopped(variableScriptFile.FilePath);
809807

810808
StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync();
811809
VariableDetailsBase[] variables = await debugService.GetVariables(stackFrames[0].AutoVariables.Id, CancellationToken.None);
@@ -825,7 +823,7 @@ await debugService.SetLineBreakpointsAsync(
825823

826824
// Execute the script and wait for the breakpoint to be hit
827825
Task _ = ExecuteVariableScriptFileAsync();
828-
AssertDebuggerStopped(variableScriptFile.FilePath);
826+
await AssertDebuggerStopped(variableScriptFile.FilePath);
829827

830828
StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync();
831829
VariableDetailsBase[] variables = await debugService.GetVariables(stackFrames[0].AutoVariables.Id, CancellationToken.None);
@@ -858,7 +856,7 @@ await debugService.SetLineBreakpointsAsync(
858856

859857
// Execute the script and wait for the breakpoint to be hit
860858
Task _ = ExecuteVariableScriptFileAsync();
861-
AssertDebuggerStopped(variableScriptFile.FilePath);
859+
await AssertDebuggerStopped(variableScriptFile.FilePath);
862860

863861
StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync();
864862
VariableDetailsBase[] variables = await debugService.GetVariables(stackFrames[0].AutoVariables.Id, CancellationToken.None);
@@ -878,7 +876,7 @@ await debugService.SetLineBreakpointsAsync(
878876

879877
// Execute the script and wait for the breakpoint to be hit
880878
Task _ = ExecuteVariableScriptFileAsync();
881-
AssertDebuggerStopped(variableScriptFile.FilePath);
879+
await AssertDebuggerStopped(variableScriptFile.FilePath);
882880

883881
StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync();
884882
VariableDetailsBase[] variables = await debugService.GetVariables(stackFrames[0].AutoVariables.Id, CancellationToken.None);
@@ -905,7 +903,7 @@ public async Task DebuggerEnumerableShowsRawView()
905903

906904
// Execute the script and wait for the breakpoint to be hit
907905
Task _ = ExecuteVariableScriptFileAsync();
908-
AssertDebuggerStopped(commandBreakpointDetails: breakpoint);
906+
await AssertDebuggerStopped(commandBreakpointDetails: breakpoint);
909907

910908
VariableDetailsBase simpleArrayVar = Array.Find(
911909
await GetVariables(VariableContainerDetails.ScriptScopeName),
@@ -962,7 +960,7 @@ public async Task DebuggerDictionaryShowsRawView()
962960

963961
// Execute the script and wait for the breakpoint to be hit
964962
Task _ = ExecuteVariableScriptFileAsync();
965-
AssertDebuggerStopped(commandBreakpointDetails: breakpoint);
963+
await AssertDebuggerStopped(commandBreakpointDetails: breakpoint);
966964

967965
VariableDetailsBase simpleDictionaryVar = Array.Find(
968966
await GetVariables(VariableContainerDetails.ScriptScopeName),
@@ -1025,7 +1023,7 @@ public async Task DebuggerDerivedDictionaryPropertyInRawView()
10251023

10261024
// Execute the script and wait for the breakpoint to be hit
10271025
Task _ = ExecuteVariableScriptFileAsync();
1028-
AssertDebuggerStopped(commandBreakpointDetails: breakpoint);
1026+
await AssertDebuggerStopped(commandBreakpointDetails: breakpoint);
10291027

10301028
VariableDetailsBase sortedDictionaryVar = Array.Find(
10311029
await GetVariables(VariableContainerDetails.ScriptScopeName),
@@ -1074,7 +1072,7 @@ await debugService.SetLineBreakpointsAsync(
10741072

10751073
// Execute the script and wait for the breakpoint to be hit
10761074
Task _ = ExecuteVariableScriptFileAsync();
1077-
AssertDebuggerStopped(variableScriptFile.FilePath);
1075+
await AssertDebuggerStopped(variableScriptFile.FilePath);
10781076

10791077
StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync();
10801078
VariableDetailsBase[] variables = await debugService.GetVariables(stackFrames[0].AutoVariables.Id, CancellationToken.None);
@@ -1103,7 +1101,7 @@ await debugService.SetLineBreakpointsAsync(
11031101

11041102
// Execute the script and wait for the breakpoint to be hit
11051103
Task _ = ExecuteVariableScriptFileAsync();
1106-
AssertDebuggerStopped(variableScriptFile.FilePath);
1104+
await AssertDebuggerStopped(variableScriptFile.FilePath);
11071105

11081106
StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync();
11091107
VariableDetailsBase[] variables = await debugService.GetVariables(stackFrames[0].AutoVariables.Id, CancellationToken.None);
@@ -1132,7 +1130,7 @@ await debugService.SetCommandBreakpointsAsync(
11321130

11331131
ScriptFile testScript = GetDebugScript("GetChildItemTest.ps1");
11341132
Task _ = ExecuteScriptFileAsync(testScript.FilePath);
1135-
AssertDebuggerStopped(testScript.FilePath, 2);
1133+
await AssertDebuggerStopped(testScript.FilePath, 2);
11361134

11371135
VariableDetailsBase[] variables = await GetVariables(VariableContainerDetails.LocalScopeName);
11381136
VariableDetailsBase var = Array.Find(variables, v => v.Name == "$file");
@@ -1152,7 +1150,7 @@ public async Task DebuggerToStringShouldMarshallToPipeline()
11521150

11531151
// Execute the script and wait for the breakpoint to be hit
11541152
Task _ = ExecuteVariableScriptFileAsync();
1155-
AssertDebuggerStopped(commandBreakpointDetails: breakpoint);
1153+
await AssertDebuggerStopped(commandBreakpointDetails: breakpoint);
11561154

11571155
VariableDetailsBase[] vars = await GetVariables(VariableContainerDetails.ScriptScopeName);
11581156
VariableDetailsBase customToStrings = Array.Find(vars, i => i.Name is "$CustomToStrings");

0 commit comments

Comments
 (0)