Skip to content

Commit

Permalink
Add regression test for System.Windows.Forms bug
Browse files Browse the repository at this point in the history
  • Loading branch information
andyleejordan committed Aug 13, 2021
1 parent 3c27ec3 commit c64ca5c
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 25 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Add-Type -AssemblyName "System.Windows.Forms"
$before = "before"
$form = New-Object System.Windows.Forms.Form
$after = "after"
Write-Host "$before, $form, $after"
90 changes: 65 additions & 25 deletions test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ public class DebugServiceTests : IDisposable
private AsyncQueue<SessionStateChangedEventArgs> sessionStateQueue =
new AsyncQueue<SessionStateChangedEventArgs>();

private ScriptFile GetDebugScript(string fileName)
{
return this.workspace.GetFile(
TestUtilities.NormalizePath(Path.Combine(
Path.GetDirectoryName(typeof(DebugServiceTests).Assembly.Location),
"../../../../PowerShellEditorServices.Test.Shared/Debugging",
fileName
)));
}

public DebugServiceTests()
{
var logger = NullLogger.Instance;
Expand All @@ -41,18 +51,9 @@ public DebugServiceTests()

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

// Load the test debug file
this.debugScriptFile =
this.workspace.GetFile(
TestUtilities.NormalizePath(Path.Combine(
Path.GetDirectoryName(typeof(DebugServiceTests).Assembly.Location),
"../../../../PowerShellEditorServices.Test.Shared/Debugging/VariableTest.ps1")));

this.variableScriptFile =
this.workspace.GetFile(
TestUtilities.NormalizePath(Path.Combine(
Path.GetDirectoryName(typeof(DebugServiceTests).Assembly.Location),
"../../../../PowerShellEditorServices.Test.Shared/Debugging/VariableTest.ps1")));
// Load the test debug files
this.debugScriptFile = GetDebugScript("DebugTest.ps1");
this.variableScriptFile = GetDebugScript("VariableTest.ps1");

this.debugService = new DebugService(
this.powerShellContext,
Expand All @@ -65,13 +66,6 @@ public DebugServiceTests()

this.debugService.DebuggerStopped += debugService_DebuggerStopped;
this.debugService.BreakpointUpdated += debugService_BreakpointUpdated;

// Load the test debug file
this.debugScriptFile =
this.workspace.GetFile(
TestUtilities.NormalizePath(Path.Combine(
Path.GetDirectoryName(typeof(DebugServiceTests).Assembly.Location),
"../../../../PowerShellEditorServices.Test.Shared/Debugging/DebugTest.ps1")));
}

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

await this.debugService.SetLineBreakpointsAsync(
debugWithParamsFile,
Expand Down Expand Up @@ -889,7 +879,7 @@ await this.debugService.SetLineBreakpointsAsync(

var nullStringVar = variables.FirstOrDefault(v => v.Name == "$nullString");
Assert.NotNull(nullStringVar);
Assert.True("[NullString]".Equals(nullStringVar.ValueString));
Assert.Equal("[NullString]", nullStringVar.ValueString);
Assert.True(nullStringVar.IsExpandable);

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

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

// This is a regression test for a bug where user code causes a new synchronization context
// to be created, breaking the extension. It's most evident when debugging PowerShell
// scripts that use System.Windows.Forms. It required fixing both Editor Services and
// OmniSharp.
//
// This test depends on PowerShell being able to load System.Windows.Forms, which only works
// reliably with Windows PowerShell. It works with PowerShell Core in the real-world;
// however, our host executable is xUnit, not PowerShell. So by restricting to Windows
// PowerShell, we avoid all issues with our test project (and the xUnit executable) not
// having System.Windows.Forms deployed, and can instead rely on the Windows Global Assembly
// Cache (GAC) to find it.
[Trait("Category", "DebugService")]
[SkippableFact]
public async Task DebuggerStepsPastSystemWindowsForms()
{
Skip.If(!VersionUtils.IsPS5);

ScriptFile formsFile = GetDebugScript("FormsTest.ps1");

await debugService.SetCommandBreakpointsAsync(
new [] { CommandBreakpointDetails.Create("Write-Host") }).ConfigureAwait(false);

Task executeTask = powerShellContext.ExecuteScriptWithArgsAsync(formsFile.FilePath);

// Wait for function breakpoint to hit
await AssertDebuggerStopped(formsFile.FilePath, 5).ConfigureAwait(false);

StackFrameDetails[] stackFrames = debugService.GetStackFrames();
VariableDetailsBase[] variables =
debugService.GetVariables(stackFrames[0].LocalVariables.Id);

// Verify $before, $form, and $after vars exist
var before = variables.FirstOrDefault(v => v.Name == "$before");
Assert.NotNull(before);
Assert.Equal("\"before\"", before.ValueString);

var form = variables.FirstOrDefault(v => v.Name == "$form");
Assert.NotNull(form);
Assert.StartsWith("System.Windows.Forms.Form", form.ValueString);

var after = variables.FirstOrDefault(v => v.Name == "$after");
Assert.NotNull(after);
Assert.Equal("\"after\"", after.ValueString);

// Abort script execution early and wait for completion
debugService.Abort();
await executeTask.ConfigureAwait(false);
}

public async Task AssertDebuggerPaused()
{
DebuggerStoppedEventArgs eventArgs =
Expand Down

0 comments on commit c64ca5c

Please sign in to comment.