mirror of
https://github.com/PowerShell/PowerShell.git
synced 2025-01-22 16:23:28 +08:00
New New-PSBreakpoint
cmdlet & new -Breakpoint
parameter for Debug-Runspace
(#8923)
This PR does 4 things: * Adds a new cmdlet `New-PSBreakpoint` which creates new `Breakpoint` objects and writes them to the pipeline * Adds a `-Breakpoint` parameter to `Debug-Runspace` which will receive `Breakpoint` objects * Makes the constructors for `*Breakpoint` public for use with the API * Makes `Debugger.GetBreakpoint(string id)` and `Debugger.GetBreakpoints()` public since `SetBreakpoints` is public Note: `New-PSBreakpoint` and `Set-PSBreakpoint` (which already exists) are similar... but `Set-PSBreakpoint` also sets the breakpoints in the _current_ runspace. This is not ideal if we want to set breakpoints in a _different runspace than the current one_. ## PR Context The "Attach to process" debugging experience in the PowerShell extension for VSCode is _ok_ but it's not great. The reason it's not great is due to the `BreakAll` feature of PowerShell debugging which, when you run `Debug-Runspace`, will break at the first piece of code that gets run. This is not ideal when you "Attach to process" _and then_ run your code in the other runspace. Today, the experience drops you in `PSReadLine`'s psm1 if PSRL is available or in the vscode PowerShell helper psm1. It's unexpected for the user and not ideal. This PR will allow the extension to pass in the breakpoints that need to be set initially with `BreakAll` turned off for none of this silly behavior. ### Silly behavior example If you want a repro, try this: PowerShell instance 1: ``` Enter-PSHostProcess -Id $otherprocesspid Debug-Runspace 1 ``` PowerShell instance 2: ``` ./runfoo.ps1 ``` Note that you end up NOT `runfoo.ps1`
This commit is contained in:
parent
d67ee7aee3
commit
13fd3af810
@ -100,6 +100,21 @@ namespace Microsoft.PowerShell.Commands
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The optional breakpoint objects to use for debugging.
|
||||
/// </summary>
|
||||
[Experimental("Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints", ExperimentAction.Show)]
|
||||
[Parameter(Position = 1,
|
||||
ParameterSetName = DebugRunspaceCommand.InstanceIdParameterSet)]
|
||||
[Parameter(ParameterSetName = DebugRunspaceCommand.RunspaceParameterSet)]
|
||||
[Parameter(ParameterSetName = DebugRunspaceCommand.IdParameterSet)]
|
||||
[Parameter(ParameterSetName = DebugRunspaceCommand.NameParameterSet)]
|
||||
public Breakpoint[] Breakpoint
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides
|
||||
@ -260,7 +275,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
_debugger.SetDebugMode(DebugModes.LocalScript | DebugModes.RemoteScript);
|
||||
|
||||
// Set up host script debugger to debug the runspace.
|
||||
_debugger.DebugRunspace(_runspace);
|
||||
_debugger.DebugRunspace(_runspace, disableBreakAll: Breakpoint?.Length > 0);
|
||||
|
||||
while (_debugging)
|
||||
{
|
||||
@ -517,6 +532,10 @@ namespace Microsoft.PowerShell.Commands
|
||||
{
|
||||
SetLocalMode(runspace.Debugger, true);
|
||||
EnableHostDebugger(runspace, false);
|
||||
if (Breakpoint?.Length > 0)
|
||||
{
|
||||
runspace.Debugger?.SetBreakpoints(Breakpoint);
|
||||
}
|
||||
}
|
||||
|
||||
private void RestoreRunspace(Runspace runspace)
|
||||
|
@ -350,6 +350,22 @@ namespace Microsoft.PowerShell.Commands
|
||||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The optional breakpoint objects to use for debugging.
|
||||
/// </summary>
|
||||
[Experimental("Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints", ExperimentAction.Show)]
|
||||
[Parameter(Position = 1,
|
||||
ParameterSetName = CommonRunspaceCommandBase.RunspaceParameterSet)]
|
||||
[Parameter(Position = 1,
|
||||
ParameterSetName = CommonRunspaceCommandBase.RunspaceNameParameterSet)]
|
||||
[Parameter(Position = 1,
|
||||
ParameterSetName = CommonRunspaceCommandBase.RunspaceIdParameterSet)]
|
||||
public Breakpoint[] Breakpoint
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides
|
||||
@ -362,58 +378,61 @@ namespace Microsoft.PowerShell.Commands
|
||||
if (this.ParameterSetName.Equals(CommonRunspaceCommandBase.ProcessNameParameterSet))
|
||||
{
|
||||
SetDebugPreferenceHelper(ProcessName, AppDomainName, true, "EnableRunspaceDebugCommandPersistDebugPreferenceFailure");
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
IReadOnlyList<Runspace> results = GetRunspaces();
|
||||
|
||||
foreach (var runspace in results)
|
||||
{
|
||||
IReadOnlyList<Runspace> results = GetRunspaces();
|
||||
|
||||
foreach (var runspace in results)
|
||||
if (runspace.RunspaceStateInfo.State != RunspaceState.Opened)
|
||||
{
|
||||
if (runspace.RunspaceStateInfo.State != RunspaceState.Opened)
|
||||
WriteError(
|
||||
new ErrorRecord(new PSInvalidOperationException(string.Format(CultureInfo.InvariantCulture, Debugger.RunspaceOptionInvalidRunspaceState, runspace.Name)),
|
||||
"SetRunspaceDebugOptionCommandInvalidRunspaceState",
|
||||
ErrorCategory.InvalidOperation,
|
||||
this));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
System.Management.Automation.Debugger debugger = GetDebuggerFromRunspace(runspace);
|
||||
if (debugger == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Enable debugging by preserving debug stop events.
|
||||
debugger.UnhandledBreakpointMode = UnhandledBreakpointProcessingMode.Wait;
|
||||
|
||||
if (this.MyInvocation.BoundParameters.ContainsKey(nameof(BreakAll)))
|
||||
{
|
||||
if (BreakAll)
|
||||
{
|
||||
WriteError(
|
||||
new ErrorRecord(new PSInvalidOperationException(string.Format(CultureInfo.InvariantCulture, Debugger.RunspaceOptionInvalidRunspaceState, runspace.Name)),
|
||||
"SetRunspaceDebugOptionCommandInvalidRunspaceState",
|
||||
ErrorCategory.InvalidOperation,
|
||||
this)
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
System.Management.Automation.Debugger debugger = GetDebuggerFromRunspace(runspace);
|
||||
if (debugger == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Enable debugging by preserving debug stop events.
|
||||
debugger.UnhandledBreakpointMode = UnhandledBreakpointProcessingMode.Wait;
|
||||
|
||||
if (this.MyInvocation.BoundParameters.ContainsKey(nameof(BreakAll)))
|
||||
{
|
||||
if (BreakAll)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
debugger.SetDebuggerStepMode(true);
|
||||
}
|
||||
catch (PSInvalidOperationException e)
|
||||
{
|
||||
WriteError(
|
||||
new ErrorRecord(
|
||||
e,
|
||||
"SetRunspaceDebugOptionCommandCannotEnableDebuggerStepping",
|
||||
ErrorCategory.InvalidOperation,
|
||||
this)
|
||||
);
|
||||
}
|
||||
debugger.SetDebuggerStepMode(true);
|
||||
}
|
||||
else
|
||||
catch (PSInvalidOperationException e)
|
||||
{
|
||||
debugger.SetDebuggerStepMode(false);
|
||||
WriteError(
|
||||
new ErrorRecord(
|
||||
e,
|
||||
"SetRunspaceDebugOptionCommandCannotEnableDebuggerStepping",
|
||||
ErrorCategory.InvalidOperation,
|
||||
this));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
debugger.SetDebuggerStepMode(false);
|
||||
}
|
||||
}
|
||||
|
||||
// If any breakpoints were provided, set those in the debugger.
|
||||
if (Breakpoint?.Length > 0)
|
||||
{
|
||||
debugger.SetBreakpoints(Breakpoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,101 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Management.Automation;
|
||||
using System.Management.Automation.Internal;
|
||||
|
||||
namespace Microsoft.PowerShell.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// This class implements New-PSBreakpoint command.
|
||||
/// </summary>
|
||||
[Experimental("Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints", ExperimentAction.Show)]
|
||||
[Cmdlet(VerbsCommon.New, "PSBreakpoint", DefaultParameterSetName = LineParameterSetName, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113449")]
|
||||
[OutputType(typeof(VariableBreakpoint), typeof(CommandBreakpoint), typeof(LineBreakpoint))]
|
||||
public class NewPSBreakpointCommand : PSBreakpointCreationBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a new breakpoint.
|
||||
/// </summary>
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
// If there is a script, resolve its path
|
||||
Collection<string> scripts = ResolveScriptPaths();
|
||||
|
||||
// If it is a command breakpoint...
|
||||
if (ParameterSetName.Equals(CommandParameterSetName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
for (int i = 0; i < Command.Length; i++)
|
||||
{
|
||||
if (scripts.Count > 0)
|
||||
{
|
||||
foreach (string path in scripts)
|
||||
{
|
||||
WildcardPattern pattern = WildcardPattern.Get(Command[i], WildcardOptions.Compiled | WildcardOptions.IgnoreCase);
|
||||
WriteObject(new CommandBreakpoint(path, pattern, Command[i], Action));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WildcardPattern pattern = WildcardPattern.Get(Command[i], WildcardOptions.Compiled | WildcardOptions.IgnoreCase);
|
||||
WriteObject(new CommandBreakpoint(null, pattern, Command[i], Action));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ParameterSetName.Equals(VariableParameterSetName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// If it is a variable breakpoint...
|
||||
for (int i = 0; i < Variable.Length; i++)
|
||||
{
|
||||
if (scripts.Count > 0)
|
||||
{
|
||||
foreach (string path in scripts)
|
||||
{
|
||||
WriteObject(new VariableBreakpoint(path, Variable[i], Mode, Action));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteObject(new VariableBreakpoint(null, Variable[i], Mode, Action));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Else it is the default parameter set (Line breakpoint)...
|
||||
Debug.Assert(ParameterSetName.Equals(LineParameterSetName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
for (int i = 0; i < Line.Length; i++)
|
||||
{
|
||||
if (Line[i] < 1)
|
||||
{
|
||||
WriteError(
|
||||
new ErrorRecord(
|
||||
new ArgumentException(Debugger.LineLessThanOne),
|
||||
"NewPSBreakpoint:LineLessThanOne",
|
||||
ErrorCategory.InvalidArgument,
|
||||
null));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (string path in scripts)
|
||||
{
|
||||
if (Column != 0)
|
||||
{
|
||||
WriteObject(new LineBreakpoint(path, Line[i], Column, Action));
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteObject(new LineBreakpoint(path, Line[i], Action));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Management.Automation;
|
||||
using System.Management.Automation.Internal;
|
||||
|
||||
namespace Microsoft.PowerShell.Commands
|
||||
{
|
||||
/// <summary>
|
||||
/// Base class for Set/New-PSBreakpoint.
|
||||
/// </summary>
|
||||
public class PSBreakpointCreationBase : PSCmdlet
|
||||
{
|
||||
internal const string CommandParameterSetName = "Command";
|
||||
internal const string LineParameterSetName = "Line";
|
||||
internal const string VariableParameterSetName = "Variable";
|
||||
|
||||
#region parameters
|
||||
|
||||
/// <summary>
|
||||
/// The action to take when hitting this breakpoint.
|
||||
/// </summary>
|
||||
[Parameter(ParameterSetName = CommandParameterSetName)]
|
||||
[Parameter(ParameterSetName = LineParameterSetName)]
|
||||
[Parameter(ParameterSetName = VariableParameterSetName)]
|
||||
public ScriptBlock Action { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The column to set the breakpoint on.
|
||||
/// </summary>
|
||||
[Parameter(Position = 2, ParameterSetName = LineParameterSetName)]
|
||||
[ValidateRange(1, int.MaxValue)]
|
||||
public int Column { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The command(s) to set the breakpoint on.
|
||||
/// </summary>
|
||||
[Alias("C")]
|
||||
[Parameter(ParameterSetName = CommandParameterSetName, Mandatory = true)]
|
||||
public string[] Command { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The line to set the breakpoint on.
|
||||
/// </summary>
|
||||
[Parameter(Position = 1, ParameterSetName = LineParameterSetName, Mandatory = true)]
|
||||
public int[] Line { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The script to set the breakpoint on.
|
||||
/// </summary>
|
||||
[Parameter(ParameterSetName = CommandParameterSetName, Position = 0)]
|
||||
[Parameter(ParameterSetName = LineParameterSetName, Mandatory = true, Position = 0)]
|
||||
[Parameter(ParameterSetName = VariableParameterSetName, Position = 0)]
|
||||
[ValidateNotNull]
|
||||
public string[] Script { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The variables to set the breakpoint(s) on.
|
||||
/// </summary>
|
||||
[Alias("V")]
|
||||
[Parameter(ParameterSetName = VariableParameterSetName, Mandatory = true)]
|
||||
public string[] Variable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The access type for variable breakpoints to break on.
|
||||
/// </summary>
|
||||
[Parameter(ParameterSetName = VariableParameterSetName)]
|
||||
public VariableAccessMode Mode { get; set; } = VariableAccessMode.Write;
|
||||
|
||||
#endregion parameters
|
||||
|
||||
internal Collection<string> ResolveScriptPaths()
|
||||
{
|
||||
Collection<string> scripts = new Collection<string>();
|
||||
|
||||
if (Script != null)
|
||||
{
|
||||
foreach (string script in Script)
|
||||
{
|
||||
Collection<PathInfo> scriptPaths = SessionState.Path.GetResolvedPSPathFromPSPath(script);
|
||||
|
||||
for (int i = 0; i < scriptPaths.Count; i++)
|
||||
{
|
||||
string providerPath = scriptPaths[i].ProviderPath;
|
||||
|
||||
if (!File.Exists(providerPath))
|
||||
{
|
||||
WriteError(
|
||||
new ErrorRecord(
|
||||
new ArgumentException(StringUtil.Format(Debugger.FileDoesNotExist, providerPath)),
|
||||
"NewPSBreakpoint:FileDoesNotExist",
|
||||
ErrorCategory.InvalidArgument,
|
||||
null));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
string extension = Path.GetExtension(providerPath);
|
||||
|
||||
if (!extension.Equals(".ps1", StringComparison.OrdinalIgnoreCase) && !extension.Equals(".psm1", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
WriteError(
|
||||
new ErrorRecord(
|
||||
new ArgumentException(StringUtil.Format(Debugger.WrongExtension, providerPath)),
|
||||
"NewPSBreakpoint:WrongExtension",
|
||||
ErrorCategory.InvalidArgument,
|
||||
null));
|
||||
continue;
|
||||
}
|
||||
|
||||
scripts.Add(Path.GetFullPath(providerPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return scripts;
|
||||
}
|
||||
}
|
||||
}
|
@ -13,79 +13,10 @@ namespace Microsoft.PowerShell.Commands
|
||||
/// <summary>
|
||||
/// This class implements Set-PSBreakpoint command.
|
||||
/// </summary>
|
||||
[Cmdlet(VerbsCommon.Set, "PSBreakpoint", DefaultParameterSetName = "Line", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113449")]
|
||||
[Cmdlet(VerbsCommon.Set, "PSBreakpoint", DefaultParameterSetName = LineParameterSetName, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113449")]
|
||||
[OutputType(typeof(VariableBreakpoint), typeof(CommandBreakpoint), typeof(LineBreakpoint))]
|
||||
public class SetPSBreakpointCommand : PSCmdlet
|
||||
public class SetPSBreakpointCommand : PSBreakpointCreationBase
|
||||
{
|
||||
#region parameters
|
||||
|
||||
/// <summary>
|
||||
/// The action to take when hitting this breakpoint.
|
||||
/// </summary>
|
||||
[Parameter(ParameterSetName = "Command")]
|
||||
[Parameter(ParameterSetName = "Line")]
|
||||
[Parameter(ParameterSetName = "Variable")]
|
||||
public ScriptBlock Action { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// The column to set the breakpoint on.
|
||||
/// </summary>
|
||||
[Parameter(Position = 2, ParameterSetName = "Line")]
|
||||
[ValidateRange(1, int.MaxValue)]
|
||||
public int Column
|
||||
{
|
||||
get
|
||||
{
|
||||
return _column ?? 0;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
_column = value;
|
||||
}
|
||||
}
|
||||
|
||||
private int? _column = null;
|
||||
|
||||
/// <summary>
|
||||
/// The command(s) to set the breakpoint on.
|
||||
/// </summary>
|
||||
[Alias("C")]
|
||||
[Parameter(ParameterSetName = "Command", Mandatory = true)]
|
||||
[ValidateNotNull]
|
||||
public string[] Command { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// The line to set the breakpoint on.
|
||||
/// </summary>
|
||||
[Parameter(Position = 1, ParameterSetName = "Line", Mandatory = true)]
|
||||
[ValidateNotNull]
|
||||
public int[] Line { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// The script to set the breakpoint on.
|
||||
/// </summary>
|
||||
[Parameter(ParameterSetName = "Command", Position = 0)]
|
||||
[Parameter(ParameterSetName = "Line", Mandatory = true, Position = 0)]
|
||||
[Parameter(ParameterSetName = "Variable", Position = 0)]
|
||||
[ValidateNotNull]
|
||||
public string[] Script { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// The variables to set the breakpoint(s) on.
|
||||
/// </summary>
|
||||
[Alias("V")]
|
||||
[Parameter(ParameterSetName = "Variable", Mandatory = true)]
|
||||
[ValidateNotNull]
|
||||
public string[] Variable { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[Parameter(ParameterSetName = "Variable")]
|
||||
public VariableAccessMode Mode { get; set; } = VariableAccessMode.Write;
|
||||
|
||||
#endregion parameters
|
||||
|
||||
/// <summary>
|
||||
/// Verifies that debugging is supported.
|
||||
/// </summary>
|
||||
@ -130,52 +61,12 @@ namespace Microsoft.PowerShell.Commands
|
||||
protected override void ProcessRecord()
|
||||
{
|
||||
// If there is a script, resolve its path
|
||||
Collection<string> scripts = new Collection<string>();
|
||||
|
||||
if (Script != null)
|
||||
{
|
||||
foreach (string script in Script)
|
||||
{
|
||||
Collection<PathInfo> scriptPaths = SessionState.Path.GetResolvedPSPathFromPSPath(script);
|
||||
|
||||
for (int i = 0; i < scriptPaths.Count; i++)
|
||||
{
|
||||
string providerPath = scriptPaths[i].ProviderPath;
|
||||
|
||||
if (!File.Exists(providerPath))
|
||||
{
|
||||
WriteError(
|
||||
new ErrorRecord(
|
||||
new ArgumentException(StringUtil.Format(Debugger.FileDoesNotExist, providerPath)),
|
||||
"SetPSBreakpoint:FileDoesNotExist",
|
||||
ErrorCategory.InvalidArgument,
|
||||
null));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
string extension = Path.GetExtension(providerPath);
|
||||
|
||||
if (!extension.Equals(".ps1", StringComparison.OrdinalIgnoreCase) && !extension.Equals(".psm1", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
WriteError(
|
||||
new ErrorRecord(
|
||||
new ArgumentException(StringUtil.Format(Debugger.WrongExtension, providerPath)),
|
||||
"SetPSBreakpoint:WrongExtension",
|
||||
ErrorCategory.InvalidArgument,
|
||||
null));
|
||||
continue;
|
||||
}
|
||||
|
||||
scripts.Add(Path.GetFullPath(providerPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
Collection<string> scripts = ResolveScriptPaths();
|
||||
|
||||
//
|
||||
// If it is a command breakpoint...
|
||||
//
|
||||
if (ParameterSetName.Equals("Command", StringComparison.OrdinalIgnoreCase))
|
||||
if (ParameterSetName.Equals(CommandParameterSetName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
for (int i = 0; i < Command.Length; i++)
|
||||
{
|
||||
@ -197,7 +88,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
//
|
||||
// If it is a variable breakpoint...
|
||||
//
|
||||
else if (ParameterSetName.Equals("Variable", StringComparison.OrdinalIgnoreCase))
|
||||
else if (ParameterSetName.Equals(VariableParameterSetName, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
for (int i = 0; i < Variable.Length; i++)
|
||||
{
|
||||
@ -221,7 +112,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
//
|
||||
else
|
||||
{
|
||||
Debug.Assert(ParameterSetName.Equals("Line", StringComparison.OrdinalIgnoreCase));
|
||||
Debug.Assert(ParameterSetName.Equals(LineParameterSetName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
for (int i = 0; i < Line.Length; i++)
|
||||
{
|
||||
@ -239,7 +130,7 @@ namespace Microsoft.PowerShell.Commands
|
||||
|
||||
foreach (string path in scripts)
|
||||
{
|
||||
if (_column != null)
|
||||
if (Column != 0)
|
||||
{
|
||||
WriteObject(
|
||||
Context.Debugger.NewStatementBreakpoint(path, Line[i], Column, Action));
|
||||
|
@ -18,7 +18,7 @@ CmdletsToExport = @(
|
||||
'Set-MarkdownOption', 'Add-Member', 'Get-Member', 'Compare-Object', 'Group-Object', 'Measure-Object',
|
||||
'New-Object', 'Select-Object', 'Sort-Object', 'Tee-Object', 'Register-ObjectEvent', 'Write-Output',
|
||||
'Import-PowerShellDataFile', 'Write-Progress', 'Disable-PSBreakpoint', 'Enable-PSBreakpoint',
|
||||
'Get-PSBreakpoint', 'Remove-PSBreakpoint', 'Set-PSBreakpoint', 'Get-PSCallStack', 'Export-PSSession',
|
||||
'Get-PSBreakpoint', 'Remove-PSBreakpoint', 'Set-PSBreakpoint', 'New-PSBreakpoint', 'Get-PSCallStack', 'Export-PSSession',
|
||||
'Import-PSSession', 'Get-Random', 'Invoke-RestMethod', 'Debug-Runspace', 'Get-Runspace',
|
||||
'Disable-RunspaceDebug', 'Enable-RunspaceDebug', 'Get-RunspaceDebug', 'Start-Sleep', 'Join-String',
|
||||
'Out-String', 'Select-String', 'ConvertFrom-StringData', 'Format-Table', 'New-TemporaryFile', 'New-TimeSpan',
|
||||
@ -31,4 +31,14 @@ FunctionsToExport = @()
|
||||
AliasesToExport = @('fhx')
|
||||
NestedModules = @("Microsoft.PowerShell.Commands.Utility.dll")
|
||||
HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=855960'
|
||||
PrivateData = @{
|
||||
PSData = @{
|
||||
ExperimentalFeatures = @(
|
||||
@{
|
||||
Name = 'Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints'
|
||||
Description = "Enables the New-PSBreakpoint cmdlet and the -Breakpoint parameter on Debug-Runspace to set breakpoints in another Runspace upfront."
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ CmdletsToExport = @(
|
||||
'Show-Markdown', 'Get-MarkdownOption', 'Set-MarkdownOption', 'Add-Member', 'Get-Member', 'Compare-Object', 'Group-Object',
|
||||
'Measure-Object', 'New-Object', 'Select-Object', 'Sort-Object', 'Tee-Object', 'Register-ObjectEvent', 'Write-Output',
|
||||
'Import-PowerShellDataFile', 'Write-Progress', 'Disable-PSBreakpoint', 'Enable-PSBreakpoint', 'Get-PSBreakpoint',
|
||||
'Remove-PSBreakpoint', 'Set-PSBreakpoint', 'Get-PSCallStack', 'Export-PSSession', 'Import-PSSession', 'Get-Random',
|
||||
'Remove-PSBreakpoint', 'Set-PSBreakpoint', 'New-PSBreakpoint', 'Get-PSCallStack', 'Export-PSSession', 'Import-PSSession', 'Get-Random',
|
||||
'Invoke-RestMethod', 'Debug-Runspace', 'Get-Runspace', 'Disable-RunspaceDebug', 'Enable-RunspaceDebug',
|
||||
'Get-RunspaceDebug', 'ConvertFrom-SddlString', 'Start-Sleep', 'Join-String', 'Out-String', 'Select-String',
|
||||
'ConvertFrom-StringData', 'Format-Table', 'New-TemporaryFile', 'New-TimeSpan', 'Get-TraceSource', 'Set-TraceSource',
|
||||
|
@ -4553,6 +4553,7 @@ end {
|
||||
new SessionStateAliasEntry("mi", "Move-Item", string.Empty, ReadOnly),
|
||||
new SessionStateAliasEntry("mp", "Move-ItemProperty", string.Empty, ReadOnly),
|
||||
new SessionStateAliasEntry("nal", "New-Alias", string.Empty, ReadOnly),
|
||||
new SessionStateAliasEntry("nbp", "New-PSBreakpoint", string.Empty, ReadOnly),
|
||||
new SessionStateAliasEntry("ndr", "New-PSDrive", string.Empty, ReadOnly),
|
||||
new SessionStateAliasEntry("ni", "New-Item", string.Empty, ReadOnly),
|
||||
new SessionStateAliasEntry("nv", "New-Variable", string.Empty, ReadOnly),
|
||||
|
@ -6,6 +6,7 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Management.Automation.Internal;
|
||||
using System.Management.Automation.Language;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Management.Automation
|
||||
{
|
||||
@ -58,16 +59,36 @@ namespace System.Management.Automation
|
||||
|
||||
#region constructors
|
||||
|
||||
internal Breakpoint(string script, ScriptBlock action)
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="Breakpoint"/>
|
||||
/// </summary>
|
||||
protected Breakpoint(string script)
|
||||
: this(script, null)
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="Breakpoint"/>
|
||||
/// </summary>
|
||||
protected Breakpoint(string script, ScriptBlock action)
|
||||
{
|
||||
Enabled = true;
|
||||
Script = script;
|
||||
Id = s_lastID++;
|
||||
Id = Interlocked.Increment(ref s_lastID);
|
||||
Action = action;
|
||||
HitCount = 0;
|
||||
}
|
||||
|
||||
internal Breakpoint(string script, ScriptBlock action, int id)
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="Breakpoint"/>
|
||||
/// </summary>
|
||||
protected Breakpoint(string script, int id)
|
||||
: this(script, null, id)
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="Breakpoint"/>
|
||||
/// </summary>
|
||||
protected Breakpoint(string script, ScriptBlock action, int id)
|
||||
{
|
||||
Enabled = true;
|
||||
Script = script;
|
||||
@ -135,14 +156,34 @@ namespace System.Management.Automation
|
||||
/// </summary>
|
||||
public class CommandBreakpoint : Breakpoint
|
||||
{
|
||||
internal CommandBreakpoint(string script, WildcardPattern command, string commandString, ScriptBlock action)
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="CommandBreakpoint"/>
|
||||
/// </summary>
|
||||
public CommandBreakpoint(string script, WildcardPattern command, string commandString)
|
||||
: this(script, command, commandString, null)
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="CommandBreakpoint"/>
|
||||
/// </summary>
|
||||
public CommandBreakpoint(string script, WildcardPattern command, string commandString, ScriptBlock action)
|
||||
: base(script, action)
|
||||
{
|
||||
CommandPattern = command;
|
||||
Command = commandString;
|
||||
}
|
||||
|
||||
internal CommandBreakpoint(string script, WildcardPattern command, string commandString, ScriptBlock action, int id)
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="CommandBreakpoint"/>
|
||||
/// </summary>
|
||||
public CommandBreakpoint(string script, WildcardPattern command, string commandString, int id)
|
||||
: this(script, command, commandString, null, id)
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="CommandBreakpoint"/>
|
||||
/// </summary>
|
||||
public CommandBreakpoint(string script, WildcardPattern command, string commandString, ScriptBlock action, int id)
|
||||
: base(script, action, id)
|
||||
{
|
||||
CommandPattern = command;
|
||||
@ -224,7 +265,7 @@ namespace System.Management.Automation
|
||||
/// </summary>
|
||||
Read,
|
||||
/// <summary>
|
||||
/// Break on write access only (default)
|
||||
/// Break on write access only (default).
|
||||
/// </summary>
|
||||
Write,
|
||||
/// <summary>
|
||||
@ -238,14 +279,34 @@ namespace System.Management.Automation
|
||||
/// </summary>
|
||||
public class VariableBreakpoint : Breakpoint
|
||||
{
|
||||
internal VariableBreakpoint(string script, string variable, VariableAccessMode accessMode, ScriptBlock action)
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="VariableBreakpoint"/>.
|
||||
/// </summary>
|
||||
public VariableBreakpoint(string script, string variable, VariableAccessMode accessMode)
|
||||
: this(script, variable, accessMode, null)
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="VariableBreakpoint"/>.
|
||||
/// </summary>
|
||||
public VariableBreakpoint(string script, string variable, VariableAccessMode accessMode, ScriptBlock action)
|
||||
: base(script, action)
|
||||
{
|
||||
Variable = variable;
|
||||
AccessMode = accessMode;
|
||||
}
|
||||
|
||||
internal VariableBreakpoint(string script, string variable, VariableAccessMode accessMode, ScriptBlock action, int id)
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="VariableBreakpoint"/>.
|
||||
/// </summary>
|
||||
public VariableBreakpoint(string script, string variable, VariableAccessMode accessMode, int id)
|
||||
: this(script, variable, accessMode, null, id)
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="VariableBreakpoint"/>.
|
||||
/// </summary>
|
||||
public VariableBreakpoint(string script, string variable, VariableAccessMode accessMode, ScriptBlock action, int id)
|
||||
: base(script, action, id)
|
||||
{
|
||||
Variable = variable;
|
||||
@ -300,7 +361,17 @@ namespace System.Management.Automation
|
||||
/// </summary>
|
||||
public class LineBreakpoint : Breakpoint
|
||||
{
|
||||
internal LineBreakpoint(string script, int line, ScriptBlock action)
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="LineBreakpoint"/>
|
||||
/// </summary>
|
||||
public LineBreakpoint(string script, int line)
|
||||
: this(script, line, null)
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="LineBreakpoint"/>
|
||||
/// </summary>
|
||||
public LineBreakpoint(string script, int line, ScriptBlock action)
|
||||
: base(script, action)
|
||||
{
|
||||
Diagnostics.Assert(!string.IsNullOrEmpty(script), "Caller to verify script parameter is not null or empty.");
|
||||
@ -309,7 +380,17 @@ namespace System.Management.Automation
|
||||
SequencePointIndex = -1;
|
||||
}
|
||||
|
||||
internal LineBreakpoint(string script, int line, int column, ScriptBlock action)
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="LineBreakpoint"/>
|
||||
/// </summary>
|
||||
public LineBreakpoint(string script, int line, int column)
|
||||
: this(script, line, column, null)
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="LineBreakpoint"/>
|
||||
/// </summary>
|
||||
public LineBreakpoint(string script, int line, int column, ScriptBlock action)
|
||||
: base(script, action)
|
||||
{
|
||||
Diagnostics.Assert(!string.IsNullOrEmpty(script), "Caller to verify script parameter is not null or empty.");
|
||||
@ -318,7 +399,17 @@ namespace System.Management.Automation
|
||||
SequencePointIndex = -1;
|
||||
}
|
||||
|
||||
internal LineBreakpoint(string script, int line, int column, ScriptBlock action, int id)
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="LineBreakpoint"/>
|
||||
/// </summary>
|
||||
public LineBreakpoint(string script, int line, int column, int id)
|
||||
: this(script, line, column, null, id)
|
||||
{}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of a <see cref="LineBreakpoint"/>
|
||||
/// </summary>
|
||||
public LineBreakpoint(string script, int line, int column, ScriptBlock action, int id)
|
||||
: base(script, action, id)
|
||||
{
|
||||
Diagnostics.Assert(!string.IsNullOrEmpty(script), "Caller to verify script parameter is not null or empty.");
|
||||
|
@ -626,6 +626,23 @@ namespace System.Management.Automation
|
||||
throw new PSNotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a breakpoint by id, primarily for Enable/Disable/Remove-PSBreakpoint cmdlets.
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the breakpoint you want.</param>
|
||||
public virtual Breakpoint GetBreakpoint(int id)
|
||||
{
|
||||
throw new PSNotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns breakpoints primarily for the Get-PSBreakpoint cmdlet.
|
||||
/// </summary>
|
||||
public virtual List<Breakpoint> GetBreakpoints()
|
||||
{
|
||||
throw new PSNotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resets the command processor source information so that it is
|
||||
/// updated with latest information on the next debug stop.
|
||||
@ -742,6 +759,16 @@ namespace System.Management.Automation
|
||||
throw new PSNotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up debugger to debug provided Runspace in a nested debug session.
|
||||
/// </summary>
|
||||
/// <param name="runspace">Runspace to debug.</param>
|
||||
/// <param name="disableBreakAll"></param>
|
||||
internal virtual void DebugRunspace(Runspace runspace, bool disableBreakAll)
|
||||
{
|
||||
throw new PSNotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the provided Runspace from the nested "active" debugger state.
|
||||
/// </summary>
|
||||
@ -822,11 +849,11 @@ namespace System.Management.Automation
|
||||
{
|
||||
_context = context;
|
||||
_inBreakpoint = false;
|
||||
_idToBreakpoint = new Dictionary<int, Breakpoint>();
|
||||
_pendingBreakpoints = new List<LineBreakpoint>();
|
||||
_boundBreakpoints = new Dictionary<string, Tuple<WeakReference, List<LineBreakpoint>>>(StringComparer.OrdinalIgnoreCase);
|
||||
_commandBreakpoints = new List<CommandBreakpoint>();
|
||||
_variableBreakpoints = new Dictionary<string, List<VariableBreakpoint>>(StringComparer.OrdinalIgnoreCase);
|
||||
_idToBreakpoint = new ConcurrentDictionary<int, Breakpoint>();
|
||||
_pendingBreakpoints = new ConcurrentDictionary<int, LineBreakpoint>();
|
||||
_boundBreakpoints = new ConcurrentDictionary<string, Tuple<WeakReference, ConcurrentDictionary<int, LineBreakpoint>>>(StringComparer.OrdinalIgnoreCase);
|
||||
_commandBreakpoints = new ConcurrentDictionary<int, CommandBreakpoint>();
|
||||
_variableBreakpoints = new ConcurrentDictionary<string, ConcurrentDictionary<int, VariableBreakpoint>>(StringComparer.OrdinalIgnoreCase);
|
||||
_steppingMode = SteppingMode.None;
|
||||
_callStack = new CallStackList { _callStackList = new List<CallStackInfo>() };
|
||||
|
||||
@ -1061,10 +1088,10 @@ namespace System.Management.Automation
|
||||
|
||||
internal void RegisterScriptFile(string path, string scriptContents)
|
||||
{
|
||||
Tuple<WeakReference, List<LineBreakpoint>> boundBreakpoints;
|
||||
Tuple<WeakReference, ConcurrentDictionary<int, LineBreakpoint>> boundBreakpoints;
|
||||
if (!_boundBreakpoints.TryGetValue(path, out boundBreakpoints))
|
||||
{
|
||||
_boundBreakpoints.Add(path, Tuple.Create(new WeakReference(scriptContents), new List<LineBreakpoint>()));
|
||||
_boundBreakpoints[path] = Tuple.Create(new WeakReference(scriptContents), new ConcurrentDictionary<int, LineBreakpoint>());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1073,8 +1100,8 @@ namespace System.Management.Automation
|
||||
boundBreakpoints.Item1.TryGetTarget(out oldScriptContents);
|
||||
if (oldScriptContents == null || !oldScriptContents.Equals(scriptContents, StringComparison.Ordinal))
|
||||
{
|
||||
UnbindBoundBreakpoints(boundBreakpoints.Item2);
|
||||
_boundBreakpoints[path] = Tuple.Create(new WeakReference(scriptContents), new List<LineBreakpoint>());
|
||||
UnbindBoundBreakpoints(boundBreakpoints.Item2.Values.ToList());
|
||||
_boundBreakpoints[path] = Tuple.Create(new WeakReference(scriptContents), new ConcurrentDictionary<int, LineBreakpoint>());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1097,7 +1124,7 @@ namespace System.Management.Automation
|
||||
private Breakpoint AddCommandBreakpoint(CommandBreakpoint breakpoint)
|
||||
{
|
||||
AddBreakpointCommon(breakpoint);
|
||||
_commandBreakpoints.Add(breakpoint);
|
||||
_commandBreakpoints[breakpoint.Id] = breakpoint;
|
||||
return breakpoint;
|
||||
}
|
||||
|
||||
@ -1116,7 +1143,7 @@ namespace System.Management.Automation
|
||||
private Breakpoint AddLineBreakpoint(LineBreakpoint breakpoint)
|
||||
{
|
||||
AddBreakpointCommon(breakpoint);
|
||||
_pendingBreakpoints.Add(breakpoint);
|
||||
_pendingBreakpoints[breakpoint.Id] = breakpoint;
|
||||
return breakpoint;
|
||||
}
|
||||
|
||||
@ -1161,14 +1188,13 @@ namespace System.Management.Automation
|
||||
{
|
||||
AddBreakpointCommon(breakpoint);
|
||||
|
||||
List<VariableBreakpoint> breakpoints;
|
||||
if (!_variableBreakpoints.TryGetValue(breakpoint.Variable, out breakpoints))
|
||||
if (!_variableBreakpoints.TryGetValue(breakpoint.Variable, out ConcurrentDictionary<int, VariableBreakpoint> breakpoints))
|
||||
{
|
||||
breakpoints = new List<VariableBreakpoint>();
|
||||
_variableBreakpoints.Add(breakpoint.Variable, breakpoints);
|
||||
breakpoints = new ConcurrentDictionary<int, VariableBreakpoint>();
|
||||
_variableBreakpoints[breakpoint.Variable] = breakpoints;
|
||||
}
|
||||
|
||||
breakpoints.Add(breakpoint);
|
||||
breakpoints[breakpoint.Id] = breakpoint;
|
||||
return breakpoint;
|
||||
}
|
||||
|
||||
@ -1198,7 +1224,7 @@ namespace System.Management.Automation
|
||||
// This is the implementation of the Remove-PSBreakpoint cmdlet.
|
||||
internal void RemoveBreakpoint(Breakpoint breakpoint)
|
||||
{
|
||||
_idToBreakpoint.Remove(breakpoint.Id);
|
||||
_idToBreakpoint.Remove(breakpoint.Id, out _);
|
||||
|
||||
breakpoint.RemoveSelf(this);
|
||||
|
||||
@ -1213,22 +1239,22 @@ namespace System.Management.Automation
|
||||
|
||||
internal void RemoveVariableBreakpoint(VariableBreakpoint breakpoint)
|
||||
{
|
||||
_variableBreakpoints[breakpoint.Variable].Remove(breakpoint);
|
||||
_variableBreakpoints[breakpoint.Variable].Remove(breakpoint.Id, out _);
|
||||
}
|
||||
|
||||
internal void RemoveCommandBreakpoint(CommandBreakpoint breakpoint)
|
||||
{
|
||||
_commandBreakpoints.Remove(breakpoint);
|
||||
_commandBreakpoints.Remove(breakpoint.Id, out _);
|
||||
}
|
||||
|
||||
internal void RemoveLineBreakpoint(LineBreakpoint breakpoint)
|
||||
{
|
||||
_pendingBreakpoints.Remove(breakpoint);
|
||||
_pendingBreakpoints.Remove(breakpoint.Id, out _);
|
||||
|
||||
Tuple<WeakReference, List<LineBreakpoint>> value;
|
||||
Tuple<WeakReference, ConcurrentDictionary<int, LineBreakpoint>> value;
|
||||
if (_boundBreakpoints.TryGetValue(breakpoint.Script, out value))
|
||||
{
|
||||
value.Item2.Remove(breakpoint);
|
||||
value.Item2.Remove(breakpoint.Id, out _);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1255,7 +1281,7 @@ namespace System.Management.Automation
|
||||
}
|
||||
|
||||
List<Breakpoint> breakpoints =
|
||||
_commandBreakpoints.Where(bp => bp.Enabled && bp.Trigger(invocationInfo)).ToList<Breakpoint>();
|
||||
_commandBreakpoints.Values.Where(bp => bp.Enabled && bp.Trigger(invocationInfo)).ToList<Breakpoint>();
|
||||
|
||||
bool checkLineBp = true;
|
||||
if (breakpoints.Any())
|
||||
@ -1308,7 +1334,7 @@ namespace System.Management.Automation
|
||||
{
|
||||
SetInternalDebugMode(InternalDebugMode.Disabled);
|
||||
|
||||
List<VariableBreakpoint> breakpoints;
|
||||
ConcurrentDictionary<int, VariableBreakpoint> breakpoints;
|
||||
if (!_variableBreakpoints.TryGetValue(variableName, out breakpoints))
|
||||
{
|
||||
// $PSItem is an alias for $_. We don't use PSItem internally, but a user might
|
||||
@ -1324,7 +1350,7 @@ namespace System.Management.Automation
|
||||
|
||||
var callStackInfo = _callStack.Last();
|
||||
var currentScriptFile = (callStackInfo != null) ? callStackInfo.File : null;
|
||||
return breakpoints.Where(bp => bp.Trigger(currentScriptFile, read: read)).ToList();
|
||||
return breakpoints.Values.Where(bp => bp.Trigger(currentScriptFile, read: read)).ToList();
|
||||
}
|
||||
finally
|
||||
{
|
||||
@ -1342,17 +1368,17 @@ namespace System.Management.Automation
|
||||
/// <summary>
|
||||
/// Get a breakpoint by id, primarily for Enable/Disable/Remove-PSBreakpoint cmdlets.
|
||||
/// </summary>
|
||||
internal Breakpoint GetBreakpoint(int id)
|
||||
/// <param name="id">Id of the breakpoint you want.</param>
|
||||
public override Breakpoint GetBreakpoint(int id)
|
||||
{
|
||||
Breakpoint breakpoint;
|
||||
_idToBreakpoint.TryGetValue(id, out breakpoint);
|
||||
_idToBreakpoint.TryGetValue(id, out Breakpoint breakpoint);
|
||||
return breakpoint;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns breakpoints primarily for the Get-PSBreakpoint cmdlet.
|
||||
/// </summary>
|
||||
internal List<Breakpoint> GetBreakpoints()
|
||||
public override List<Breakpoint> GetBreakpoints()
|
||||
{
|
||||
return (from bp in _idToBreakpoint.Values orderby bp.Id select bp).ToList();
|
||||
}
|
||||
@ -1499,7 +1525,7 @@ namespace System.Management.Automation
|
||||
if (string.IsNullOrEmpty(functionContext._file)) { return; }
|
||||
|
||||
bool havePendingBreakpoint = false;
|
||||
foreach (var item in _pendingBreakpoints)
|
||||
foreach ((int breakpointId, LineBreakpoint item) in _pendingBreakpoints)
|
||||
{
|
||||
if (item.IsScriptBreakpoint && item.Script.Equals(functionContext._file, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
@ -1618,11 +1644,11 @@ namespace System.Management.Automation
|
||||
}
|
||||
|
||||
private readonly ExecutionContext _context;
|
||||
private List<LineBreakpoint> _pendingBreakpoints;
|
||||
private readonly Dictionary<string, Tuple<WeakReference, List<LineBreakpoint>>> _boundBreakpoints;
|
||||
private readonly List<CommandBreakpoint> _commandBreakpoints;
|
||||
private readonly Dictionary<string, List<VariableBreakpoint>> _variableBreakpoints;
|
||||
private readonly Dictionary<int, Breakpoint> _idToBreakpoint;
|
||||
private ConcurrentDictionary<int, LineBreakpoint> _pendingBreakpoints;
|
||||
private readonly ConcurrentDictionary<string, Tuple<WeakReference, ConcurrentDictionary<int, LineBreakpoint>>> _boundBreakpoints;
|
||||
private readonly ConcurrentDictionary<int, CommandBreakpoint> _commandBreakpoints;
|
||||
private readonly ConcurrentDictionary<string, ConcurrentDictionary<int, VariableBreakpoint>> _variableBreakpoints;
|
||||
private readonly ConcurrentDictionary<int, Breakpoint> _idToBreakpoint;
|
||||
private SteppingMode _steppingMode;
|
||||
private CallStackInfo _overOrOutFrame;
|
||||
private CallStackList _callStack;
|
||||
@ -1935,7 +1961,7 @@ namespace System.Management.Automation
|
||||
breakpoint.SequencePoints = null;
|
||||
breakpoint.SequencePointIndex = -1;
|
||||
breakpoint.BreakpointBitArray = null;
|
||||
_pendingBreakpoints.Add(breakpoint);
|
||||
_pendingBreakpoints[breakpoint.Id] = breakpoint;
|
||||
}
|
||||
|
||||
boundBreakpoints.Clear();
|
||||
@ -1946,7 +1972,7 @@ namespace System.Management.Automation
|
||||
if (!_pendingBreakpoints.Any())
|
||||
return;
|
||||
|
||||
var newPendingBreakpoints = new List<LineBreakpoint>();
|
||||
var newPendingBreakpoints = new Dictionary<int, LineBreakpoint>();
|
||||
var currentScriptFile = functionContext._file;
|
||||
|
||||
// If we're not in a file, we can't have any line breakpoints.
|
||||
@ -1967,7 +1993,7 @@ namespace System.Management.Automation
|
||||
|
||||
Diagnostics.Assert(tuple.Item1 == functionContext._boundBreakpoints, "What's up?");
|
||||
|
||||
foreach (var breakpoint in _pendingBreakpoints)
|
||||
foreach ((int breakpointId, LineBreakpoint breakpoint) in _pendingBreakpoints)
|
||||
{
|
||||
bool bound = false;
|
||||
if (breakpoint.TrySetBreakpoint(currentScriptFile, functionContext))
|
||||
@ -1983,17 +2009,16 @@ namespace System.Management.Automation
|
||||
// We need to keep track of any breakpoints that are bound in each script because they may
|
||||
// need to be rebound if the script changes.
|
||||
var boundBreakpoints = _boundBreakpoints[currentScriptFile].Item2;
|
||||
Diagnostics.Assert(boundBreakpoints.IndexOf(breakpoint) < 0, "Don't add more than once.");
|
||||
boundBreakpoints.Add(breakpoint);
|
||||
boundBreakpoints[breakpoint.Id] = breakpoint;
|
||||
}
|
||||
|
||||
if (!bound)
|
||||
{
|
||||
newPendingBreakpoints.Add(breakpoint);
|
||||
newPendingBreakpoints.Add(breakpoint.Id, breakpoint);
|
||||
}
|
||||
}
|
||||
|
||||
_pendingBreakpoints = newPendingBreakpoints;
|
||||
_pendingBreakpoints = new ConcurrentDictionary<int, LineBreakpoint>(newPendingBreakpoints);
|
||||
}
|
||||
|
||||
private void StopOnSequencePoint(FunctionContext functionContext, List<Breakpoint> breakpoints)
|
||||
@ -2355,24 +2380,20 @@ namespace System.Management.Automation
|
||||
{
|
||||
if (_idToBreakpoint.ContainsKey(breakpoint.Id)) { continue; }
|
||||
|
||||
LineBreakpoint lineBp = breakpoint as LineBreakpoint;
|
||||
if (lineBp != null)
|
||||
switch (breakpoint)
|
||||
{
|
||||
AddLineBreakpoint(lineBp);
|
||||
continue;
|
||||
}
|
||||
|
||||
CommandBreakpoint cmdBp = breakpoint as CommandBreakpoint;
|
||||
if (cmdBp != null)
|
||||
{
|
||||
AddCommandBreakpoint(cmdBp);
|
||||
continue;
|
||||
}
|
||||
|
||||
VariableBreakpoint variableBp = breakpoint as VariableBreakpoint;
|
||||
if (variableBp != null)
|
||||
{
|
||||
AddVariableBreakpoint(variableBp);
|
||||
case LineBreakpoint lineBp:
|
||||
AddLineBreakpoint(lineBp);
|
||||
continue;
|
||||
case CommandBreakpoint cmdBp:
|
||||
AddCommandBreakpoint(cmdBp);
|
||||
continue;
|
||||
case VariableBreakpoint variableBp:
|
||||
AddVariableBreakpoint(variableBp);
|
||||
continue;
|
||||
default:
|
||||
// Unreachable default block
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2657,11 +2678,17 @@ namespace System.Management.Automation
|
||||
|
||||
#region Runspace Debugging
|
||||
|
||||
internal override void DebugRunspace(Runspace runspace)
|
||||
{
|
||||
DebugRunspace(runspace, disableBreakAll:false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up debugger to debug provided Runspace in a nested debug session.
|
||||
/// </summary>
|
||||
/// <param name="runspace">Runspace to debug.</param>
|
||||
internal override void DebugRunspace(Runspace runspace)
|
||||
/// <param name="disableBreakAll">When specified, it will not turn on BreakAll.</param>
|
||||
internal override void DebugRunspace(Runspace runspace, bool disableBreakAll)
|
||||
{
|
||||
if (runspace == null)
|
||||
{
|
||||
@ -2696,7 +2723,7 @@ namespace System.Management.Automation
|
||||
|
||||
AddToRunningRunspaceList(new PSStandaloneMonitorRunspaceInfo(runspace));
|
||||
|
||||
if (!runspace.Debugger.InBreakpoint)
|
||||
if (!runspace.Debugger.InBreakpoint && !disableBreakAll)
|
||||
{
|
||||
EnableDebuggerStepping(EnableNestedType.NestedRunspace);
|
||||
}
|
||||
@ -3988,6 +4015,28 @@ namespace System.Management.Automation
|
||||
return _wrappedDebugger.ProcessCommand(command, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the provided set of breakpoints to the debugger.
|
||||
/// </summary>
|
||||
/// <param name="breakpoints">Breakpoints.</param>
|
||||
public override void SetBreakpoints(IEnumerable<Breakpoint> breakpoints)
|
||||
{
|
||||
_wrappedDebugger.SetBreakpoints(breakpoints);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a breakpoint by id, primarily for Enable/Disable/Remove-PSBreakpoint cmdlets.
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the breakpoint you want.</param>
|
||||
public override Breakpoint GetBreakpoint(int id) =>
|
||||
_wrappedDebugger.GetBreakpoint(id);
|
||||
|
||||
/// <summary>
|
||||
/// Returns breakpoints primarily for the Get-PSBreakpoint cmdlet.
|
||||
/// </summary>
|
||||
public override List<Breakpoint> GetBreakpoints() =>
|
||||
_wrappedDebugger.GetBreakpoints();
|
||||
|
||||
/// <summary>
|
||||
/// SetDebuggerAction.
|
||||
/// </summary>
|
||||
|
@ -3907,6 +3907,28 @@ namespace System.Management.Automation
|
||||
return _wrappedDebugger.ProcessCommand(command, output);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the provided set of breakpoints to the debugger.
|
||||
/// </summary>
|
||||
/// <param name="breakpoints">Breakpoints.</param>
|
||||
public override void SetBreakpoints(IEnumerable<Breakpoint> breakpoints)
|
||||
{
|
||||
_wrappedDebugger.SetBreakpoints(breakpoints);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a breakpoint by id, primarily for Enable/Disable/Remove-PSBreakpoint cmdlets.
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the breakpoint you want.</param>
|
||||
public override Breakpoint GetBreakpoint(int id) =>
|
||||
_wrappedDebugger.GetBreakpoint(id);
|
||||
|
||||
/// <summary>
|
||||
/// Returns breakpoints primarily for the Get-PSBreakpoint cmdlet.
|
||||
/// </summary>
|
||||
public override List<Breakpoint> GetBreakpoints() =>
|
||||
_wrappedDebugger.GetBreakpoints();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the debugger resume action.
|
||||
/// </summary>
|
||||
|
@ -2007,6 +2007,28 @@ namespace System.Management.Automation
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the provided set of breakpoints to the debugger.
|
||||
/// </summary>
|
||||
/// <param name="breakpoints">Breakpoints.</param>
|
||||
public override void SetBreakpoints(IEnumerable<Breakpoint> breakpoints)
|
||||
{
|
||||
_runspace.Debugger?.SetBreakpoints(breakpoints);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a breakpoint by id, primarily for Enable/Disable/Remove-PSBreakpoint cmdlets.
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the breakpoint you want.</param>
|
||||
public override Breakpoint GetBreakpoint(int id) =>
|
||||
_runspace.Debugger?.GetBreakpoint(id);
|
||||
|
||||
/// <summary>
|
||||
/// Returns breakpoints primarily for the Get-PSBreakpoint cmdlet.
|
||||
/// </summary>
|
||||
public override List<Breakpoint> GetBreakpoints() =>
|
||||
_runspace.Debugger?.GetBreakpoints();
|
||||
|
||||
/// <summary>
|
||||
/// SetDebuggerAction.
|
||||
/// </summary>
|
||||
|
@ -1767,6 +1767,28 @@ namespace System.Management.Automation
|
||||
get { return _inDebugMode; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds the provided set of breakpoints to the debugger.
|
||||
/// </summary>
|
||||
/// <param name="breakpoints">Breakpoints.</param>
|
||||
public override void SetBreakpoints(IEnumerable<Breakpoint> breakpoints)
|
||||
{
|
||||
_wrappedDebugger.Value.SetBreakpoints(breakpoints);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a breakpoint by id, primarily for Enable/Disable/Remove-PSBreakpoint cmdlets.
|
||||
/// </summary>
|
||||
/// <param name="id">Id of the breakpoint you want.</param>
|
||||
public override Breakpoint GetBreakpoint(int id) =>
|
||||
_wrappedDebugger.Value.GetBreakpoint(id);
|
||||
|
||||
/// <summary>
|
||||
/// Returns breakpoints primarily for the Get-PSBreakpoint cmdlet.
|
||||
/// </summary>
|
||||
public override List<Breakpoint> GetBreakpoints() =>
|
||||
_wrappedDebugger.Value.GetBreakpoints();
|
||||
|
||||
/// <summary>
|
||||
/// Exits debugger mode with the provided resume action.
|
||||
/// </summary>
|
||||
@ -1921,6 +1943,16 @@ namespace System.Management.Automation
|
||||
_wrappedDebugger.Value.DebugRunspace(runspace);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets up debugger to debug provided Runspace in a nested debug session.
|
||||
/// </summary>
|
||||
/// <param name="runspace">Runspace to debug.</param>
|
||||
/// <param name="disableBreakAll"></param>
|
||||
internal override void DebugRunspace(Runspace runspace, bool disableBreakAll)
|
||||
{
|
||||
_wrappedDebugger.Value.DebugRunspace(runspace, disableBreakAll);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes the provided Runspace from the nested "active" debugger state.
|
||||
/// </summary>
|
||||
|
@ -0,0 +1,77 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
$FeatureEnabled = $EnabledExperimentalFeatures.Contains('Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints')
|
||||
|
||||
Describe "`Enable-RunspaceDebug -Breakpoint` Unit Tests - Feature-Enabled" -Tags "CI" {
|
||||
|
||||
BeforeAll {
|
||||
if (!$FeatureEnabled) {
|
||||
Write-Verbose "Test Suite Skipped. The test suite requires the experimental feature 'Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints' to be enabled." -Verbose
|
||||
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
|
||||
$PSDefaultParameterValues["it:skip"] = $true
|
||||
return
|
||||
}
|
||||
|
||||
#Set up script file 1
|
||||
$scriptFileName1 = Join-Path $TestDrive -ChildPath breakpointTestScript.ps1
|
||||
|
||||
$contents = @"
|
||||
function Hello
|
||||
{
|
||||
`$greeting = 'Hello, world!'
|
||||
write-host `$greeting
|
||||
}
|
||||
|
||||
function Goodbye
|
||||
{
|
||||
`$message = 'Good bye, cruel world!'
|
||||
write-host `$message
|
||||
}
|
||||
|
||||
Hello
|
||||
Goodbye
|
||||
"@
|
||||
|
||||
$contents > $scriptFileName1
|
||||
|
||||
# The breakpoints are created here because when the tests are run with the experimental feature off,
|
||||
# this command does not exist and the Pester tests fail to work
|
||||
$breakpointArr = @(
|
||||
New-PSBreakpoint -Line 12 $scriptFileName1
|
||||
New-PSBreakpoint -Line 13 $scriptFileName1
|
||||
)
|
||||
|
||||
$iss = [initialsessionstate]::CreateDefault2();
|
||||
$testRunspace1 = [runspacefactory]::CreateRunspace($iss)
|
||||
$testRunspace1.Name = "TestRunspaceDebuggerReset"
|
||||
$testRunspace1.Open()
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
if (!$FeatureEnabled) {
|
||||
$global:PSDefaultParameterValues = $originalDefaultParameterValues
|
||||
return
|
||||
}
|
||||
|
||||
# Clean up
|
||||
$testRunspace1.Dispose()
|
||||
}
|
||||
|
||||
It "Can set breakpoints in the runspace - <Name>" -TestCases @(
|
||||
@{
|
||||
Name = "Current runspace"
|
||||
Runspace = [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace
|
||||
Breakpoints = $breakpointArr
|
||||
},
|
||||
@{
|
||||
Name = $testRunspace1.Name
|
||||
Runspace = $testRunspace1
|
||||
Breakpoints = $breakpointArr
|
||||
}
|
||||
) {
|
||||
param($Runspace, $Breakpoints)
|
||||
Enable-RunspaceDebug -Breakpoint $Breakpoints -Runspace $Runspace
|
||||
$Runspace.Debugger.GetBreakpoints() | Should -Be @($Breakpoints)
|
||||
}
|
||||
}
|
@ -0,0 +1,195 @@
|
||||
# Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
# Licensed under the MIT License.
|
||||
|
||||
$FeatureEnabled = $EnabledExperimentalFeatures.Contains('Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints')
|
||||
|
||||
Describe "New-PSBreakpoint Unit Tests - Feature-Enabled" -Tags "CI" {
|
||||
|
||||
BeforeAll {
|
||||
if (!$FeatureEnabled) {
|
||||
Write-Verbose "Test Suite Skipped. The test suite requires the experimental feature 'Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints' to be enabled." -Verbose
|
||||
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
|
||||
$PSDefaultParameterValues["it:skip"] = $true
|
||||
return
|
||||
}
|
||||
|
||||
#Set up script file 1
|
||||
$scriptFileName1 = Join-Path $TestDrive -ChildPath breakpointTestScript.ps1
|
||||
|
||||
$contents = @"
|
||||
function Hello
|
||||
{
|
||||
`$greeting = 'Hello, world!'
|
||||
write-host `$greeting
|
||||
}
|
||||
|
||||
function Goodbye
|
||||
{
|
||||
`$message = 'Good bye, cruel world!'
|
||||
write-host `$message
|
||||
}
|
||||
|
||||
Hello
|
||||
Goodbye
|
||||
|
||||
# The following 2 statements produce null tokens (needed to verify 105473)
|
||||
#
|
||||
`$table = @{}
|
||||
|
||||
return
|
||||
"@
|
||||
|
||||
$contents > $scriptFileName1
|
||||
|
||||
# Set up script file 2
|
||||
$scriptFileName2 = Join-Path -Path $TestDrive -ChildPath psbreakpointtestscript.ps1
|
||||
|
||||
"`$var = 1 " > $scriptFileName2
|
||||
}
|
||||
|
||||
AfterAll {
|
||||
if (!$FeatureEnabled) {
|
||||
$global:PSDefaultParameterValues = $originalDefaultParameterValues
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
It "Should be able to set psbreakpoints for -Line" {
|
||||
$brk = New-PSBreakpoint -Line 13 -Script $scriptFileName1
|
||||
$brk.Line | Should -Be 13
|
||||
}
|
||||
|
||||
It "Should be able to set psbreakpoints for -Line and -column" {
|
||||
$brk = New-PSBreakpoint -line 13 -column 1 -script $scriptFileName1
|
||||
$brk.Line | Should -Be 13
|
||||
$brk.Column | Should -Be 1
|
||||
}
|
||||
|
||||
It "Should be able to set psbreakpoints for -Line and -action" {
|
||||
$brk = New-PSBreakpoint -line 13 -action {{ break; }} -script $scriptFileName1
|
||||
$brk.Line | Should -Be 13
|
||||
$brk.Action | Should -Match "break"
|
||||
}
|
||||
|
||||
It "Should be able to set psbreakpoints for -Line, -column and -action" {
|
||||
$brk = New-PSBreakpoint -line 13 -column 1 -action {{ break; }} -script $scriptFileName1
|
||||
$brk.Line | Should -Be 13
|
||||
$brk.Column | Should -Be 1
|
||||
$brk.Action | Should -Match "break"
|
||||
}
|
||||
|
||||
It "-script and -line can take multiple items" {
|
||||
$brk = New-PSBreakpoint -line 11,12,13 -column 1 -script $scriptFileName1,$scriptFileName1
|
||||
$brk.Line | Should -BeIn 11,12,13
|
||||
$brk.Column | Should -BeIn 1
|
||||
}
|
||||
|
||||
It "-script and -line are positional" {
|
||||
$brk = New-PSBreakpoint $scriptFileName1 13
|
||||
$brk.Line | Should -Be 13
|
||||
}
|
||||
|
||||
It "-script, -line and -column are positional" {
|
||||
$brk = New-PSBreakpoint $scriptFileName1 13 1
|
||||
$brk.Line | Should -Be 13
|
||||
$brk.Column | Should -Be 1
|
||||
}
|
||||
|
||||
It "Should throw Exception when missing mandatory parameter -line" -Pending {
|
||||
$output = pwsh -noninteractive -command "nbp -column 1 -script $scriptFileName1"
|
||||
[system.string]::Join(" ", $output) | Should -Match "MissingMandatoryParameter,Microsoft.PowerShell.Commands.NewPSBreakpointCommand"
|
||||
}
|
||||
|
||||
It "Should throw Exception when missing mandatory parameter" -Pending {
|
||||
$output = pwsh -noprofile -noninteractive -command "nbp -line 1"
|
||||
[system.string]::Join(" ", $output) | Should -Match "MissingMandatoryParameter,Microsoft.PowerShell.Commands.NewPSBreakpointCommand"
|
||||
}
|
||||
|
||||
It "Should be able to set psbreakpoints for -command" {
|
||||
$brk = New-PSBreakpoint -command "write-host"
|
||||
$brk.Command | Should -BeExactly "write-host"
|
||||
}
|
||||
|
||||
It "Should be able to set psbreakpoints for -command, -script" {
|
||||
$brk = New-PSBreakpoint -command "write-host" -script $scriptFileName1
|
||||
$brk.Command | Should -BeExactly "write-host"
|
||||
}
|
||||
|
||||
It "Should be able to set psbreakpoints for -command, -action and -script" {
|
||||
$brk = New-PSBreakpoint -command "write-host" -action {{ break; }} -script $scriptFileName1
|
||||
$brk.Action | Should -Match "break"
|
||||
}
|
||||
|
||||
It "-Command can take multiple items" {
|
||||
$brk = New-PSBreakpoint -command write-host,Hello
|
||||
$brk.Command | Should -Be write-host,Hello
|
||||
}
|
||||
|
||||
It "-Script is positional" {
|
||||
$brk = New-PSBreakpoint -command "Hello" $scriptFileName1
|
||||
$brk.Command | Should -BeExactly "Hello"
|
||||
|
||||
$brk = New-PSBreakpoint $scriptFileName1 -command "Hello"
|
||||
$brk.Command | Should -BeExactly "Hello"
|
||||
}
|
||||
|
||||
It "Should be able to set breakpoints on functions" {
|
||||
$brk = New-PSBreakpoint -command Hello,Goodbye -script $scriptFileName1
|
||||
$brk.Command | Should -Be Hello,Goodbye
|
||||
}
|
||||
|
||||
It "Should be throw Exception when Column number less than 1" {
|
||||
{ New-PSBreakpoint -line 1 -column -1 -script $scriptFileName1 } | Should -Throw -ErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.Commands.NewPSBreakpointCommand"
|
||||
}
|
||||
|
||||
It "Should be throw Exception when Line number less than 1" {
|
||||
$ErrorActionPreference = "Stop"
|
||||
{ New-PSBreakpoint -line -1 -script $scriptFileName1 } | Should -Throw -ErrorId "NewPSBreakpoint:LineLessThanOne,Microsoft.PowerShell.Commands.NewPSBreakpointCommand"
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
}
|
||||
|
||||
It "Fail to set psbreakpoints when script is a file of wrong type" {
|
||||
$tempFile = [System.IO.Path]::GetTempFileName()
|
||||
$ErrorActionPreference = "Stop"
|
||||
{
|
||||
New-PSBreakpoint -Script $tempFile -Line 1
|
||||
} | Should -Throw
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
Remove-Item $tempFile -Force
|
||||
}
|
||||
|
||||
It "Fail to set psbreakpoints when script file does not exist" {
|
||||
$ErrorActionPreference = "Stop"
|
||||
${script.ps1} = 10
|
||||
{
|
||||
New-PSBreakpoint -Script variable:\script.ps1 -Line 1
|
||||
} | Should -Throw
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
}
|
||||
|
||||
It "Should be able to set a psbreakpoint on a line" {
|
||||
$lineNumber = 1
|
||||
$brk = New-PSBreakpoint -Line $lineNumber -Script $scriptFileName2
|
||||
$brk.Line | Should -Be $lineNumber
|
||||
}
|
||||
|
||||
It "Should throw when a string is entered for a line number" {
|
||||
{
|
||||
$lineNumber = "one"
|
||||
New-PSBreakpoint -Line $lineNumber -Script $scriptFileName2
|
||||
|
||||
} | Should -Throw
|
||||
}
|
||||
|
||||
It "Should be able to set a psbreakpoint on a Command" {
|
||||
$command = "theCommand"
|
||||
$brk = New-PSBreakpoint -Command $command -Script $scriptFileName2
|
||||
$brk.Command | Should -Be $command
|
||||
}
|
||||
|
||||
It "Should be able to set a psbreakpoint on a variable" {
|
||||
$var = "theVariable"
|
||||
$brk = New-PSBreakpoint -Command $var -Script $scriptFileName2
|
||||
$brk.Command | Should -Be $var
|
||||
}
|
||||
}
|
@ -105,6 +105,7 @@ Describe "Verify approved aliases list" -Tags "CI" {
|
||||
"Alias", "move", "Move-Item", $($FullCLR -or $CoreWindows -or $CoreUnix), "", "AllScope", ""
|
||||
"Alias", "mp", "Move-ItemProperty", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "", ""
|
||||
"Alias", "mv", "Move-Item", $($FullCLR -or $CoreWindows ), "", "", ""
|
||||
"Alias", "nbp", "New-PSBreakpoint", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "", ""
|
||||
"Alias", "nal", "New-Alias", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "", ""
|
||||
"Alias", "ndr", "New-PSDrive", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "", ""
|
||||
"Alias", "ni", "New-Item", $($FullCLR -or $CoreWindows -or $CoreUnix), "ReadOnly", "", ""
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"ExperimentalFeatures": {
|
||||
"Microsoft.PowerShell.Utility.PSDebugRunspaceWithBreakpoints": ["test/powershell/Modules/Microsoft.PowerShell.Utility/New-PSBreakpoint.Tests.ps1"],
|
||||
"ExpTest.FeatureOne": [ "test/powershell/engine/ExperimentalFeature/ExperimentalFeature.Basic.Tests.ps1" ]
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT License.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Management.Automation;
|
||||
using System.Management.Automation.Runspaces;
|
||||
using Xunit;
|
||||
@ -100,5 +101,31 @@ namespace PSTests.Sequential
|
||||
runspace.Close();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestRunspaceSetBreakpoints()
|
||||
{
|
||||
using (var runspace = RunspaceFactory.CreateRunspace())
|
||||
{
|
||||
var expectedBreakpoints = new Breakpoint[] {
|
||||
new LineBreakpoint(@"./path/to/some/file.ps1", 1),
|
||||
new CommandBreakpoint(@"./path/to/some/file.ps1", new WildcardPattern("Write-Host"), "Write-Host"),
|
||||
};
|
||||
|
||||
runspace.Open();
|
||||
|
||||
try
|
||||
{
|
||||
runspace.Debugger.SetBreakpoints(expectedBreakpoints);
|
||||
List<Breakpoint> actualBreakpoints = runspace.Debugger.GetBreakpoints();
|
||||
Assert.Equal(expectedBreakpoints.Length, actualBreakpoints.Count);
|
||||
Assert.Equal(expectedBreakpoints, actualBreakpoints);
|
||||
}
|
||||
finally
|
||||
{
|
||||
runspace.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user