Skip to content

Commit eec176d

Browse files
committed
Add DebuggerBreaksInUntitledScript unit test
1 parent 2a861fb commit eec176d

File tree

2 files changed

+44
-5
lines changed

2 files changed

+44
-5
lines changed

src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,17 @@ public Task<ConfigurationDoneResponse> Handle(ConfigurationDoneArguments request
102102
return Task.FromResult(new ConfigurationDoneResponse());
103103
}
104104

105-
private async Task LaunchScriptAsync(string scriptToLaunch)
105+
// NOTE: We test this function in `DebugServiceTests` so it both needs to be internal, and
106+
// use conditional-access on `_debugStateService` and `_debugAdapterServer` as its not set
107+
// by tests.
108+
internal async Task LaunchScriptAsync(string scriptToLaunch)
106109
{
107110
PSCommand command;
108111
if (System.IO.File.Exists(scriptToLaunch))
109112
{
110113
// For a saved file we just execute its path (after escaping it).
111114
command = PSCommandHelpers.BuildDotSourceCommandWithArguments(
112-
string.Concat('"', scriptToLaunch, '"'), _debugStateService.Arguments);
115+
string.Concat('"', scriptToLaunch, '"'), _debugStateService?.Arguments);
113116
}
114117
else // It's a URI to an untitled script, or a raw script.
115118
{
@@ -135,7 +138,7 @@ private async Task LaunchScriptAsync(string scriptToLaunch)
135138
// on each invocation, so passing the user's arguments directly in the initial
136139
// `AddScript` surprisingly works.
137140
command = PSCommandHelpers
138-
.BuildDotSourceCommandWithArguments("$args[0]", _debugStateService.Arguments)
141+
.BuildDotSourceCommandWithArguments("$args[0]", _debugStateService?.Arguments)
139142
.AddArgument(ast.GetScriptBlock());
140143
}
141144
else
@@ -148,15 +151,16 @@ private async Task LaunchScriptAsync(string scriptToLaunch)
148151
"{" + System.Environment.NewLine,
149152
isScriptFile ? untitledScript.Contents : scriptToLaunch,
150153
System.Environment.NewLine + "}"),
151-
_debugStateService.Arguments);
154+
_debugStateService?.Arguments);
152155
}
153156
}
154157

155158
await _executionService.ExecutePSCommandAsync(
156159
command,
157160
CancellationToken.None,
158161
s_debuggerExecutionOptions).ConfigureAwait(false);
159-
_debugAdapterServer.SendNotification(EventNames.Terminated);
162+
163+
_debugAdapterServer?.SendNotification(EventNames.Terminated);
160164
}
161165
}
162166
}

test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Threading;
1111
using System.Threading.Tasks;
1212
using Microsoft.Extensions.Logging.Abstractions;
13+
using Microsoft.PowerShell.EditorServices.Handlers;
1314
using Microsoft.PowerShell.EditorServices.Services;
1415
using Microsoft.PowerShell.EditorServices.Services.DebugAdapter;
1516
using Microsoft.PowerShell.EditorServices.Services.PowerShell.Host;
@@ -521,6 +522,40 @@ await debugService.SetCommandBreakpointsAsync(
521522
Assert.Equal("\"True > \"", prompt.ValueString);
522523
}
523524

525+
[SkippableFact]
526+
public async Task DebuggerBreaksInUntitledScript()
527+
{
528+
Skip.IfNot(VersionUtils.PSEdition == "Core", "Untitled script breakpoints only supported in PowerShell Core");
529+
const string contents = "Write-Output $($MyInvocation.Line)";
530+
const string scriptPath = "untitled:Untitled-1";
531+
Assert.True(ScriptFile.IsUntitledPath(scriptPath));
532+
ScriptFile scriptFile = workspace.GetFileBuffer(scriptPath, contents);
533+
Assert.Equal(scriptFile.DocumentUri, scriptPath);
534+
Assert.Equal(scriptFile.Contents, contents);
535+
Assert.True(workspace.TryGetFile(scriptPath, out ScriptFile _));
536+
537+
await debugService.SetCommandBreakpointsAsync(
538+
new[] { CommandBreakpointDetails.Create("Write-Output") }).ConfigureAwait(true);
539+
540+
ConfigurationDoneHandler configurationDoneHandler = new(
541+
NullLoggerFactory.Instance, null, debugService, null, null, psesHost, workspace, null, psesHost);
542+
543+
Task _ = configurationDoneHandler.LaunchScriptAsync(scriptPath);
544+
AssertDebuggerStopped(scriptPath, 1);
545+
546+
VariableDetailsBase[] variables = GetVariables(VariableContainerDetails.CommandVariablesName);
547+
VariableDetailsBase myInvocation = Array.Find(variables, v => v.Name == "$MyInvocation");
548+
Assert.NotNull(myInvocation);
549+
Assert.True(myInvocation.IsExpandable);
550+
551+
// Here we're asserting that our hacky workaround to support breakpoints in untitled
552+
// scripts is working, namely that we're actually dot-sourcing our first argument, which
553+
// should be a cached script block. See the `LaunchScriptAsync` for more info.
554+
VariableDetailsBase[] myInvocationChildren = debugService.GetVariables(myInvocation.Id);
555+
VariableDetailsBase myInvocationLine = Array.Find(myInvocationChildren, v => v.Name == "Line");
556+
Assert.Equal("\". $args[0]\"", myInvocationLine.ValueString);
557+
}
558+
524559
[Fact]
525560
public async Task DebuggerVariableStringDisplaysCorrectly()
526561
{

0 commit comments

Comments
 (0)