Skip to content

Commit c64ca5c

Browse files
committed
Add regression test for System.Windows.Forms bug
1 parent 3c27ec3 commit c64ca5c

File tree

2 files changed

+70
-25
lines changed

2 files changed

+70
-25
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Add-Type -AssemblyName "System.Windows.Forms"
2+
$before = "before"
3+
$form = New-Object System.Windows.Forms.Form
4+
$after = "after"
5+
Write-Host "$before, $form, $after"

test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs

+65-25
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ public class DebugServiceTests : IDisposable
3232
private AsyncQueue<SessionStateChangedEventArgs> sessionStateQueue =
3333
new AsyncQueue<SessionStateChangedEventArgs>();
3434

35+
private ScriptFile GetDebugScript(string fileName)
36+
{
37+
return this.workspace.GetFile(
38+
TestUtilities.NormalizePath(Path.Combine(
39+
Path.GetDirectoryName(typeof(DebugServiceTests).Assembly.Location),
40+
"../../../../PowerShellEditorServices.Test.Shared/Debugging",
41+
fileName
42+
)));
43+
}
44+
3545
public DebugServiceTests()
3646
{
3747
var logger = NullLogger.Instance;
@@ -41,18 +51,9 @@ public DebugServiceTests()
4151

4252
this.workspace = new WorkspaceService(NullLoggerFactory.Instance);
4353

44-
// Load the test debug file
45-
this.debugScriptFile =
46-
this.workspace.GetFile(
47-
TestUtilities.NormalizePath(Path.Combine(
48-
Path.GetDirectoryName(typeof(DebugServiceTests).Assembly.Location),
49-
"../../../../PowerShellEditorServices.Test.Shared/Debugging/VariableTest.ps1")));
50-
51-
this.variableScriptFile =
52-
this.workspace.GetFile(
53-
TestUtilities.NormalizePath(Path.Combine(
54-
Path.GetDirectoryName(typeof(DebugServiceTests).Assembly.Location),
55-
"../../../../PowerShellEditorServices.Test.Shared/Debugging/VariableTest.ps1")));
54+
// Load the test debug files
55+
this.debugScriptFile = GetDebugScript("DebugTest.ps1");
56+
this.variableScriptFile = GetDebugScript("VariableTest.ps1");
5657

5758
this.debugService = new DebugService(
5859
this.powerShellContext,
@@ -65,13 +66,6 @@ public DebugServiceTests()
6566

6667
this.debugService.DebuggerStopped += debugService_DebuggerStopped;
6768
this.debugService.BreakpointUpdated += debugService_BreakpointUpdated;
68-
69-
// Load the test debug file
70-
this.debugScriptFile =
71-
this.workspace.GetFile(
72-
TestUtilities.NormalizePath(Path.Combine(
73-
Path.GetDirectoryName(typeof(DebugServiceTests).Assembly.Location),
74-
"../../../../PowerShellEditorServices.Test.Shared/Debugging/DebugTest.ps1")));
7569
}
7670

7771
async void powerShellContext_SessionStateChanged(object sender, SessionStateChangedEventArgs e)
@@ -123,11 +117,7 @@ public async Task DebuggerAcceptsScriptArgs(string[] args)
123117
// The path is intentionally odd (some escaped chars but not all) because we are testing
124118
// the internal path escaping mechanism - it should escape certains chars ([, ] and space) but
125119
// it should not escape already escaped chars.
126-
ScriptFile debugWithParamsFile =
127-
this.workspace.GetFile(
128-
TestUtilities.NormalizePath(Path.Combine(
129-
Path.GetDirectoryName(typeof(DebugServiceTests).Assembly.Location),
130-
"../../../../PowerShellEditorServices.Test.Shared/Debugging/Debug W&ith Params [Test].ps1")));
120+
ScriptFile debugWithParamsFile = GetDebugScript("Debug W&ith Params [Test].ps1");
131121

132122
await this.debugService.SetLineBreakpointsAsync(
133123
debugWithParamsFile,
@@ -889,7 +879,7 @@ await this.debugService.SetLineBreakpointsAsync(
889879

890880
var nullStringVar = variables.FirstOrDefault(v => v.Name == "$nullString");
891881
Assert.NotNull(nullStringVar);
892-
Assert.True("[NullString]".Equals(nullStringVar.ValueString));
882+
Assert.Equal("[NullString]", nullStringVar.ValueString);
893883
Assert.True(nullStringVar.IsExpandable);
894884

895885
// Abort script execution early and wait for completion
@@ -973,6 +963,7 @@ await this.debugService.SetLineBreakpointsAsync(
973963

974964
// Verifies fix for issue #86, $proc = Get-Process foo displays just the ETS property set
975965
// and not all process properties.
966+
[Trait("Category", "DebugService")]
976967
[Fact]
977968
public async Task DebuggerVariableProcessObjDisplaysCorrectly()
978969
{
@@ -1005,6 +996,55 @@ await this.debugService.SetLineBreakpointsAsync(
1005996
await executeTask.ConfigureAwait(false);
1006997
}
1007998

999+
// This is a regression test for a bug where user code causes a new synchronization context
1000+
// to be created, breaking the extension. It's most evident when debugging PowerShell
1001+
// scripts that use System.Windows.Forms. It required fixing both Editor Services and
1002+
// OmniSharp.
1003+
//
1004+
// This test depends on PowerShell being able to load System.Windows.Forms, which only works
1005+
// reliably with Windows PowerShell. It works with PowerShell Core in the real-world;
1006+
// however, our host executable is xUnit, not PowerShell. So by restricting to Windows
1007+
// PowerShell, we avoid all issues with our test project (and the xUnit executable) not
1008+
// having System.Windows.Forms deployed, and can instead rely on the Windows Global Assembly
1009+
// Cache (GAC) to find it.
1010+
[Trait("Category", "DebugService")]
1011+
[SkippableFact]
1012+
public async Task DebuggerStepsPastSystemWindowsForms()
1013+
{
1014+
Skip.If(!VersionUtils.IsPS5);
1015+
1016+
ScriptFile formsFile = GetDebugScript("FormsTest.ps1");
1017+
1018+
await debugService.SetCommandBreakpointsAsync(
1019+
new [] { CommandBreakpointDetails.Create("Write-Host") }).ConfigureAwait(false);
1020+
1021+
Task executeTask = powerShellContext.ExecuteScriptWithArgsAsync(formsFile.FilePath);
1022+
1023+
// Wait for function breakpoint to hit
1024+
await AssertDebuggerStopped(formsFile.FilePath, 5).ConfigureAwait(false);
1025+
1026+
StackFrameDetails[] stackFrames = debugService.GetStackFrames();
1027+
VariableDetailsBase[] variables =
1028+
debugService.GetVariables(stackFrames[0].LocalVariables.Id);
1029+
1030+
// Verify $before, $form, and $after vars exist
1031+
var before = variables.FirstOrDefault(v => v.Name == "$before");
1032+
Assert.NotNull(before);
1033+
Assert.Equal("\"before\"", before.ValueString);
1034+
1035+
var form = variables.FirstOrDefault(v => v.Name == "$form");
1036+
Assert.NotNull(form);
1037+
Assert.StartsWith("System.Windows.Forms.Form", form.ValueString);
1038+
1039+
var after = variables.FirstOrDefault(v => v.Name == "$after");
1040+
Assert.NotNull(after);
1041+
Assert.Equal("\"after\"", after.ValueString);
1042+
1043+
// Abort script execution early and wait for completion
1044+
debugService.Abort();
1045+
await executeTask.ConfigureAwait(false);
1046+
}
1047+
10081048
public async Task AssertDebuggerPaused()
10091049
{
10101050
DebuggerStoppedEventArgs eventArgs =

0 commit comments

Comments
 (0)