Skip to content

Commit 8a98add

Browse files
committed
Fix for issues #86 and #89. Same fix for both where we need to unwrap PSObject when getting a variable's child variables. Also added some more unit tests for variables.
1 parent 2fafc3a commit 8a98add

File tree

3 files changed

+238
-9
lines changed

3 files changed

+238
-9
lines changed

src/PowerShellEditorServices/Debugging/VariableDetails.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -233,23 +233,30 @@ private static VariableDetails[] GetChildren(object obj)
233233
return childVariables.ToArray();
234234
}
235235

236-
PSObject psObject = obj as PSObject;
237-
IDictionary dictionary = obj as IDictionary;
238-
IEnumerable enumerable = obj as IEnumerable;
239-
240236
try
241237
{
242-
if (psObject != null)
238+
PSObject psObject = obj as PSObject;
239+
240+
if ((psObject != null) &&
241+
(psObject.TypeNames[0] == typeof(PSCustomObject).ToString()))
243242
{
244-
// PowerShell wrapped objects can have extra ETS properties so let's use
245-
// PowerShell's infrastructure to get those properties.
243+
// PowerShell PSCustomObject's properties are completely defined by the ETS type system.
246244
childVariables.AddRange(
247245
psObject
248246
.Properties
249247
.Select(p => new VariableDetails(p)));
250248
}
251249
else
252250
{
251+
// If a PSObject other than a PSCustomObject, unwrap it.
252+
if (psObject != null)
253+
{
254+
obj = psObject.BaseObject;
255+
}
256+
257+
IDictionary dictionary = obj as IDictionary;
258+
IEnumerable enumerable = obj as IEnumerable;
259+
253260
// We're in the realm of regular, unwrapped .NET objects
254261
if (dictionary != null)
255262
{

test/PowerShellEditorServices.Test.Shared/Debugging/VariableTest.ps1

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,15 @@
66
function Test-Variables
77
{
88
$strVar = "Hello"
9-
$objVar = @{ firstChild = "Child"; secondChild = 42 }
109
$arrVar = @(1, 2, $strVar, $objVar)
10+
$assocArrVar = @{ firstChild = "Child"; secondChild = 42 }
1111
$classVar = [MyClass]::new();
1212
$classVar.Name = "Test"
1313
$classVar.Number = 42;
14+
$enumVar = $ErrorActionPreference
15+
$psObjVar = New-Object -TypeName PSObject -Property @{Name = 'John'; Age = 75}
16+
$psCustomObjVar = [PSCustomObject] @{Name = 'Paul'; Age = 73}
17+
$procVar = Get-Process system
1418
Write-Output "Done"
1519
}
1620

test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs

Lines changed: 219 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,38 @@ await this.AssertStateChange(
167167
PowerShellExecutionResult.Aborted);
168168
}
169169

170+
[Fact]
171+
public async Task DebuggerVariableStringDisplaysCorrectly()
172+
{
173+
ScriptFile variablesFile =
174+
this.workspace.GetFile(
175+
@"..\..\..\PowerShellEditorServices.Test.Shared\Debugging\VariableTest.ps1");
176+
177+
await this.debugService.SetBreakpoints(
178+
variablesFile,
179+
new int[] { 18 });
180+
181+
// Execute the script and wait for the breakpoint to be hit
182+
Task executeTask =
183+
this.powerShellContext.ExecuteScriptString(
184+
variablesFile.FilePath);
185+
186+
await this.AssertDebuggerStopped(variablesFile.FilePath);
187+
188+
StackFrameDetails[] stackFrames = debugService.GetStackFrames();
189+
190+
VariableDetailsBase[] variables =
191+
debugService.GetVariables(stackFrames[0].LocalVariables.Id);
192+
193+
var var = variables.FirstOrDefault(v => v.Name == "$strVar");
194+
Assert.NotNull(var);
195+
Assert.Equal("\"Hello\"", var.ValueString);
196+
Assert.False(var.IsExpandable);
197+
198+
// Abort execution of the script
199+
this.powerShellContext.AbortExecution();
200+
}
201+
170202
[Fact]
171203
public async Task DebuggerGetsVariables()
172204
{
@@ -196,7 +228,7 @@ await this.debugService.SetBreakpoints(
196228
Assert.NotNull(strVar);
197229
Assert.False(strVar.IsExpandable);
198230

199-
var objVar = variables.FirstOrDefault(v => v.Name == "$objVar");
231+
var objVar = variables.FirstOrDefault(v => v.Name == "$assocArrVar");
200232
Assert.NotNull(objVar);
201233
Assert.True(objVar.IsExpandable);
202234

@@ -221,6 +253,192 @@ await this.debugService.SetBreakpoints(
221253
this.powerShellContext.AbortExecution();
222254
}
223255

256+
[Fact]
257+
public async Task DebuggerVariableEnumDisplaysCorrectly()
258+
{
259+
ScriptFile variablesFile =
260+
this.workspace.GetFile(
261+
@"..\..\..\PowerShellEditorServices.Test.Shared\Debugging\VariableTest.ps1");
262+
263+
await this.debugService.SetBreakpoints(
264+
variablesFile,
265+
new int[] { 18 });
266+
267+
// Execute the script and wait for the breakpoint to be hit
268+
Task executeTask =
269+
this.powerShellContext.ExecuteScriptString(
270+
variablesFile.FilePath);
271+
272+
await this.AssertDebuggerStopped(variablesFile.FilePath);
273+
274+
StackFrameDetails[] stackFrames = debugService.GetStackFrames();
275+
276+
VariableDetailsBase[] variables =
277+
debugService.GetVariables(stackFrames[0].LocalVariables.Id);
278+
279+
var var = variables.FirstOrDefault(v => v.Name == "$enumVar");
280+
Assert.NotNull(var);
281+
Assert.Equal("Continue", var.ValueString);
282+
Assert.False(var.IsExpandable);
283+
284+
// Abort execution of the script
285+
this.powerShellContext.AbortExecution();
286+
}
287+
288+
[Fact]
289+
public async Task DebuggerVariableHashtableDisplaysCorrectly()
290+
{
291+
ScriptFile variablesFile =
292+
this.workspace.GetFile(
293+
@"..\..\..\PowerShellEditorServices.Test.Shared\Debugging\VariableTest.ps1");
294+
295+
await this.debugService.SetBreakpoints(
296+
variablesFile,
297+
new int[] { 18 });
298+
299+
// Execute the script and wait for the breakpoint to be hit
300+
Task executeTask =
301+
this.powerShellContext.ExecuteScriptString(
302+
variablesFile.FilePath);
303+
304+
await this.AssertDebuggerStopped(variablesFile.FilePath);
305+
306+
StackFrameDetails[] stackFrames = debugService.GetStackFrames();
307+
308+
VariableDetailsBase[] variables =
309+
debugService.GetVariables(stackFrames[0].LocalVariables.Id);
310+
311+
var var = variables.FirstOrDefault(v => v.Name == "$assocArrVar");
312+
Assert.NotNull(var);
313+
Assert.Equal("[Hashtable: 2]", var.ValueString);
314+
Assert.True(var.IsExpandable);
315+
316+
var childVars = debugService.GetVariables(var.Id);
317+
Assert.Equal(9, childVars.Length);
318+
Assert.Equal("[0]", childVars[0].Name);
319+
Assert.Equal("[secondChild, 42]", childVars[0].ValueString);
320+
Assert.Equal("[1]", childVars[1].Name);
321+
Assert.Equal("[firstChild, \"Child\"]", childVars[1].ValueString);
322+
323+
// Abort execution of the script
324+
this.powerShellContext.AbortExecution();
325+
}
326+
327+
[Fact]
328+
public async Task DebuggerVariablePSObjectDisplaysCorrectly()
329+
{
330+
ScriptFile variablesFile =
331+
this.workspace.GetFile(
332+
@"..\..\..\PowerShellEditorServices.Test.Shared\Debugging\VariableTest.ps1");
333+
334+
await this.debugService.SetBreakpoints(
335+
variablesFile,
336+
new int[] { 18 });
337+
338+
// Execute the script and wait for the breakpoint to be hit
339+
Task executeTask =
340+
this.powerShellContext.ExecuteScriptString(
341+
variablesFile.FilePath);
342+
343+
await this.AssertDebuggerStopped(variablesFile.FilePath);
344+
345+
StackFrameDetails[] stackFrames = debugService.GetStackFrames();
346+
347+
VariableDetailsBase[] variables =
348+
debugService.GetVariables(stackFrames[0].LocalVariables.Id);
349+
350+
var var = variables.FirstOrDefault(v => v.Name == "$psObjVar");
351+
Assert.NotNull(var);
352+
Assert.Equal("@{Age=75; Name=John}", var.ValueString);
353+
Assert.True(var.IsExpandable);
354+
355+
var childVars = debugService.GetVariables(var.Id);
356+
Assert.Equal(2, childVars.Length);
357+
Assert.Equal("Age", childVars[0].Name);
358+
Assert.Equal("75", childVars[0].ValueString);
359+
Assert.Equal("Name", childVars[1].Name);
360+
Assert.Equal("\"John\"", childVars[1].ValueString);
361+
362+
// Abort execution of the script
363+
this.powerShellContext.AbortExecution();
364+
}
365+
366+
[Fact]
367+
public async Task DebuggerVariablePSCustomObjectDisplaysCorrectly()
368+
{
369+
ScriptFile variablesFile =
370+
this.workspace.GetFile(
371+
@"..\..\..\PowerShellEditorServices.Test.Shared\Debugging\VariableTest.ps1");
372+
373+
await this.debugService.SetBreakpoints(
374+
variablesFile,
375+
new int[] { 18 });
376+
377+
// Execute the script and wait for the breakpoint to be hit
378+
Task executeTask =
379+
this.powerShellContext.ExecuteScriptString(
380+
variablesFile.FilePath);
381+
382+
await this.AssertDebuggerStopped(variablesFile.FilePath);
383+
384+
StackFrameDetails[] stackFrames = debugService.GetStackFrames();
385+
386+
VariableDetailsBase[] variables =
387+
debugService.GetVariables(stackFrames[0].LocalVariables.Id);
388+
389+
var var = variables.FirstOrDefault(v => v.Name == "$psCustomObjVar");
390+
Assert.NotNull(var);
391+
Assert.Equal("@{Name=Paul; Age=73}", var.ValueString);
392+
Assert.True(var.IsExpandable);
393+
394+
var childVars = debugService.GetVariables(var.Id);
395+
Assert.Equal(2, childVars.Length);
396+
Assert.Equal("Name", childVars[0].Name);
397+
Assert.Equal("\"Paul\"", childVars[0].ValueString);
398+
Assert.Equal("Age", childVars[1].Name);
399+
Assert.Equal("73", childVars[1].ValueString);
400+
401+
// Abort execution of the script
402+
this.powerShellContext.AbortExecution();
403+
}
404+
405+
// Verifies fix for issue #86, $proc = Get-Process foo displays just the
406+
// ETS property set and not all process properties.
407+
[Fact]
408+
public async Task DebuggerVariableProcessObjDisplaysCorrectly()
409+
{
410+
ScriptFile variablesFile =
411+
this.workspace.GetFile(
412+
@"..\..\..\PowerShellEditorServices.Test.Shared\Debugging\VariableTest.ps1");
413+
414+
await this.debugService.SetBreakpoints(
415+
variablesFile,
416+
new int[] { 18 });
417+
418+
// Execute the script and wait for the breakpoint to be hit
419+
Task executeTask =
420+
this.powerShellContext.ExecuteScriptString(
421+
variablesFile.FilePath);
422+
423+
await this.AssertDebuggerStopped(variablesFile.FilePath);
424+
425+
StackFrameDetails[] stackFrames = debugService.GetStackFrames();
426+
427+
VariableDetailsBase[] variables =
428+
debugService.GetVariables(stackFrames[0].LocalVariables.Id);
429+
430+
var var = variables.FirstOrDefault(v => v.Name == "$procVar");
431+
Assert.NotNull(var);
432+
Assert.Equal("System.Diagnostics.Process (System)", var.ValueString);
433+
Assert.True(var.IsExpandable);
434+
435+
var childVars = debugService.GetVariables(var.Id);
436+
Assert.Equal(52, childVars.Length);
437+
438+
// Abort execution of the script
439+
this.powerShellContext.AbortExecution();
440+
}
441+
224442
public async Task AssertDebuggerStopped(
225443
string scriptPath,
226444
int lineNumber = -1)

0 commit comments

Comments
 (0)