Skip to content

Commit

Permalink
Merge pull request #1861 from PowerShell/andschwa/improve-untitled-test
Browse files Browse the repository at this point in the history
Add `DebuggerBreaksInUntitledScript` unit test
  • Loading branch information
andyleejordan authored Jul 25, 2022
2 parents 932d3b2 + eec176d commit e6c0083
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,17 @@ public Task<ConfigurationDoneResponse> Handle(ConfigurationDoneArguments request
return Task.FromResult(new ConfigurationDoneResponse());
}

private async Task LaunchScriptAsync(string scriptToLaunch)
// NOTE: We test this function in `DebugServiceTests` so it both needs to be internal, and
// use conditional-access on `_debugStateService` and `_debugAdapterServer` as its not set
// by tests.
internal async Task LaunchScriptAsync(string scriptToLaunch)
{
PSCommand command;
if (System.IO.File.Exists(scriptToLaunch))
{
// For a saved file we just execute its path (after escaping it).
command = PSCommandHelpers.BuildDotSourceCommandWithArguments(
string.Concat('"', scriptToLaunch, '"'), _debugStateService.Arguments);
string.Concat('"', scriptToLaunch, '"'), _debugStateService?.Arguments);
}
else // It's a URI to an untitled script, or a raw script.
{
Expand All @@ -135,7 +138,7 @@ private async Task LaunchScriptAsync(string scriptToLaunch)
// on each invocation, so passing the user's arguments directly in the initial
// `AddScript` surprisingly works.
command = PSCommandHelpers
.BuildDotSourceCommandWithArguments("$args[0]", _debugStateService.Arguments)
.BuildDotSourceCommandWithArguments("$args[0]", _debugStateService?.Arguments)
.AddArgument(ast.GetScriptBlock());
}
else
Expand All @@ -148,15 +151,16 @@ private async Task LaunchScriptAsync(string scriptToLaunch)
"{" + System.Environment.NewLine,
isScriptFile ? untitledScript.Contents : scriptToLaunch,
System.Environment.NewLine + "}"),
_debugStateService.Arguments);
_debugStateService?.Arguments);
}
}

await _executionService.ExecutePSCommandAsync(
command,
CancellationToken.None,
s_debuggerExecutionOptions).ConfigureAwait(false);
_debugAdapterServer.SendNotification(EventNames.Terminated);

_debugAdapterServer?.SendNotification(EventNames.Terminated);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -305,9 +305,8 @@ public async Task CanStepPastSystemWindowsForms()
[Fact]
public async Task CanLaunchScriptWithCommentedLastLineAsync()
{
string script = GenerateScriptFromLoggingStatements("a log statement") + "# a comment at the end";
Assert.Contains(Environment.NewLine + "# a comment", script);
Assert.EndsWith("at the end", script);
string script = GenerateScriptFromLoggingStatements("$($MyInvocation.Line)") + "# a comment at the end";
Assert.EndsWith(Environment.NewLine + "# a comment at the end", script);

// NOTE: This is horribly complicated, but the "script" parameter here is assigned to
// PsesLaunchRequestArguments.Script, which is then assigned to
Expand All @@ -317,8 +316,13 @@ public async Task CanLaunchScriptWithCommentedLastLineAsync()

ConfigurationDoneResponse configDoneResponse = await PsesDebugAdapterClient.RequestConfigurationDone(new ConfigurationDoneArguments()).ConfigureAwait(true);
Assert.NotNull(configDoneResponse);
// We can check that the script was invoked as expected, which is to dot-source a script
// block with the contents surrounded by newlines. While we can't check that the last
// line was a curly brace by itself, we did check that the contents ended with a
// comment, so if this output exists then the bug did not recur.
Assert.Collection(await GetLog().ConfigureAwait(true),
(i) => Assert.Equal("a log statement", i));
(i) => Assert.Equal(". {", i),
(i) => Assert.Equal("", i));
}

[SkippableFact]
Expand Down
35 changes: 35 additions & 0 deletions test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.PowerShell.EditorServices.Handlers;
using Microsoft.PowerShell.EditorServices.Services;
using Microsoft.PowerShell.EditorServices.Services.DebugAdapter;
using Microsoft.PowerShell.EditorServices.Services.PowerShell.Host;
Expand Down Expand Up @@ -521,6 +522,40 @@ await debugService.SetCommandBreakpointsAsync(
Assert.Equal("\"True > \"", prompt.ValueString);
}

[SkippableFact]
public async Task DebuggerBreaksInUntitledScript()
{
Skip.IfNot(VersionUtils.PSEdition == "Core", "Untitled script breakpoints only supported in PowerShell Core");
const string contents = "Write-Output $($MyInvocation.Line)";
const string scriptPath = "untitled:Untitled-1";
Assert.True(ScriptFile.IsUntitledPath(scriptPath));
ScriptFile scriptFile = workspace.GetFileBuffer(scriptPath, contents);
Assert.Equal(scriptFile.DocumentUri, scriptPath);
Assert.Equal(scriptFile.Contents, contents);
Assert.True(workspace.TryGetFile(scriptPath, out ScriptFile _));

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

ConfigurationDoneHandler configurationDoneHandler = new(
NullLoggerFactory.Instance, null, debugService, null, null, psesHost, workspace, null, psesHost);

Task _ = configurationDoneHandler.LaunchScriptAsync(scriptPath);
AssertDebuggerStopped(scriptPath, 1);

VariableDetailsBase[] variables = GetVariables(VariableContainerDetails.CommandVariablesName);
VariableDetailsBase myInvocation = Array.Find(variables, v => v.Name == "$MyInvocation");
Assert.NotNull(myInvocation);
Assert.True(myInvocation.IsExpandable);

// Here we're asserting that our hacky workaround to support breakpoints in untitled
// scripts is working, namely that we're actually dot-sourcing our first argument, which
// should be a cached script block. See the `LaunchScriptAsync` for more info.
VariableDetailsBase[] myInvocationChildren = debugService.GetVariables(myInvocation.Id);
VariableDetailsBase myInvocationLine = Array.Find(myInvocationChildren, v => v.Name == "Line");
Assert.Equal("\". $args[0]\"", myInvocationLine.ValueString);
}

[Fact]
public async Task DebuggerVariableStringDisplaysCorrectly()
{
Expand Down

0 comments on commit e6c0083

Please sign in to comment.