|
@@ -1,12 +1,117 @@
|
|
|
using Esprima;
|
|
|
using Jint.Runtime.Debugger;
|
|
|
+using System.Linq;
|
|
|
using Xunit;
|
|
|
|
|
|
namespace Jint.Tests.Runtime.Debugger
|
|
|
{
|
|
|
public class BreakPointTests
|
|
|
{
|
|
|
- [Fact]
|
|
|
+ [Fact]
|
|
|
+ public void BreakLocationsCompareEqualityByValue()
|
|
|
+ {
|
|
|
+ var loc1 = new BreakLocation(42, 23);
|
|
|
+ var loc2 = new BreakLocation(42, 23);
|
|
|
+ var loc3 = new BreakLocation(17, 7);
|
|
|
+
|
|
|
+ Assert.Equal(loc1, loc2);
|
|
|
+ Assert.True(loc1 == loc2);
|
|
|
+ Assert.True(loc2 != loc3);
|
|
|
+ Assert.False(loc1 != loc2);
|
|
|
+ Assert.False(loc2 == loc3);
|
|
|
+ }
|
|
|
+
|
|
|
+ [Fact]
|
|
|
+ public void BreakLocationsWithSourceCompareEqualityByValue()
|
|
|
+ {
|
|
|
+ var loc1 = new BreakLocation("script1", 42, 23);
|
|
|
+ var loc2 = new BreakLocation("script1", 42, 23);
|
|
|
+ var loc3 = new BreakLocation("script2", 42, 23);
|
|
|
+
|
|
|
+ Assert.Equal(loc1, loc2);
|
|
|
+ Assert.True(loc1 == loc2);
|
|
|
+ Assert.True(loc2 != loc3);
|
|
|
+ Assert.False(loc1 != loc2);
|
|
|
+ Assert.False(loc2 == loc3);
|
|
|
+ }
|
|
|
+
|
|
|
+ [Fact]
|
|
|
+ public void BreakLocationsOptionalSourceEqualityComparer()
|
|
|
+ {
|
|
|
+ var script1 = new BreakLocation("script1", 42, 23);
|
|
|
+ var script2 = new BreakLocation("script2", 42, 23);
|
|
|
+ var script2b = new BreakLocation("script2", 44, 23);
|
|
|
+ var any = new BreakLocation(null, 42, 23);
|
|
|
+
|
|
|
+ var comparer = new OptionalSourceBreakLocationEqualityComparer();
|
|
|
+ Assert.True(comparer.Equals(script1, any));
|
|
|
+ Assert.True(comparer.Equals(script2, any));
|
|
|
+ Assert.False(comparer.Equals(script1, script2));
|
|
|
+ Assert.False(comparer.Equals(script2, script2b));
|
|
|
+ Assert.Equal(comparer.GetHashCode(script1), comparer.GetHashCode(any));
|
|
|
+ Assert.Equal(comparer.GetHashCode(script1), comparer.GetHashCode(script2));
|
|
|
+ Assert.NotEqual(comparer.GetHashCode(script2), comparer.GetHashCode(script2b));
|
|
|
+ }
|
|
|
+
|
|
|
+ [Fact]
|
|
|
+ public void BreakPointReplacesPreviousBreakPoint()
|
|
|
+ {
|
|
|
+ var engine = new Engine(options => options.DebugMode());
|
|
|
+
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint(4, 5, "i === 1"));
|
|
|
+ Assert.Collection(engine.DebugHandler.BreakPoints,
|
|
|
+ breakPoint =>
|
|
|
+ {
|
|
|
+ Assert.Equal(4, breakPoint.Location.Line);
|
|
|
+ Assert.Equal(5, breakPoint.Location.Column);
|
|
|
+ Assert.Equal("i === 1", breakPoint.Condition);
|
|
|
+ });
|
|
|
+
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint(4, 5));
|
|
|
+ Assert.Collection(engine.DebugHandler.BreakPoints,
|
|
|
+ breakPoint =>
|
|
|
+ {
|
|
|
+ Assert.Equal(4, breakPoint.Location.Line);
|
|
|
+ Assert.Equal(5, breakPoint.Location.Column);
|
|
|
+ Assert.Equal(null, breakPoint.Condition);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ [Fact]
|
|
|
+ public void BreakPointRemovesBasedOnLocationEquality()
|
|
|
+ {
|
|
|
+ var engine = new Engine(options => options.DebugMode());
|
|
|
+
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint(4, 5, "i === 1"));
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint(5, 6, "j === 2"));
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint(10, 7, "x > 5"));
|
|
|
+ Assert.Equal(3, engine.DebugHandler.BreakPoints.Count);
|
|
|
+
|
|
|
+ engine.DebugHandler.BreakPoints.RemoveAt(new BreakLocation(null, 4, 5));
|
|
|
+ engine.DebugHandler.BreakPoints.RemoveAt(new BreakLocation(null, 10, 7));
|
|
|
+
|
|
|
+ Assert.Collection(engine.DebugHandler.BreakPoints,
|
|
|
+ breakPoint =>
|
|
|
+ {
|
|
|
+ Assert.Equal(5, breakPoint.Location.Line);
|
|
|
+ Assert.Equal(6, breakPoint.Location.Column);
|
|
|
+ Assert.Equal("j === 2", breakPoint.Condition);
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ [Fact]
|
|
|
+ public void BreakPointContainsBasedOnLocationEquality()
|
|
|
+ {
|
|
|
+ var engine = new Engine(options => options.DebugMode());
|
|
|
+
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint(4, 5, "i === 1"));
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint(5, 6, "j === 2"));
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint(10, 7, "x > 5"));
|
|
|
+ Assert.True(engine.DebugHandler.BreakPoints.Contains(new BreakLocation(null, 5, 6)));
|
|
|
+ Assert.False(engine.DebugHandler.BreakPoints.Contains(new BreakLocation(null, 8, 9)));
|
|
|
+ }
|
|
|
+
|
|
|
+ [Fact]
|
|
|
public void BreakPointBreaksAtPosition()
|
|
|
{
|
|
|
string script = @"let x = 1, y = 2;
|
|
@@ -20,13 +125,13 @@ x++; y *= 2;
|
|
|
bool didBreak = false;
|
|
|
engine.DebugHandler.Break += (sender, info) =>
|
|
|
{
|
|
|
- Assert.Equal(4, info.CurrentStatement.Location.Start.Line);
|
|
|
- Assert.Equal(5, info.CurrentStatement.Location.Start.Column);
|
|
|
+ Assert.Equal(4, info.Location.Start.Line);
|
|
|
+ Assert.Equal(5, info.Location.Start.Column);
|
|
|
didBreak = true;
|
|
|
return StepMode.None;
|
|
|
};
|
|
|
|
|
|
- engine.DebugHandler.BreakPoints.Add(new BreakPoint(4, 5));
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint(4, 5));
|
|
|
engine.Execute(script);
|
|
|
Assert.True(didBreak);
|
|
|
}
|
|
@@ -50,14 +155,14 @@ test(z);";
|
|
|
|
|
|
var engine = new Engine(options => { options.DebugMode(); });
|
|
|
|
|
|
- engine.DebugHandler.BreakPoints.Add(new BreakPoint("script2", 3, 0));
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint("script2", 3, 0));
|
|
|
|
|
|
bool didBreak = false;
|
|
|
engine.DebugHandler.Break += (sender, info) =>
|
|
|
{
|
|
|
- Assert.Equal("script2", info.CurrentStatement.Location.Source);
|
|
|
- Assert.Equal(3, info.CurrentStatement.Location.Start.Line);
|
|
|
- Assert.Equal(0, info.CurrentStatement.Location.Start.Column);
|
|
|
+ Assert.Equal("script2", info.Location.Source);
|
|
|
+ Assert.Equal(3, info.Location.Start.Line);
|
|
|
+ Assert.Equal(0, info.Location.Start.Column);
|
|
|
didBreak = true;
|
|
|
return StepMode.None;
|
|
|
};
|
|
@@ -99,6 +204,81 @@ debugger;
|
|
|
Assert.True(didBreak);
|
|
|
}
|
|
|
|
|
|
+ [Fact]
|
|
|
+ public void DebuggerStatementDoesNotTriggerBreakWhenStepping()
|
|
|
+ {
|
|
|
+ string script = @"'dummy';
|
|
|
+debugger;
|
|
|
+'dummy';";
|
|
|
+
|
|
|
+ var engine = new Engine(options => options
|
|
|
+ .DebugMode()
|
|
|
+ .DebuggerStatementHandling(DebuggerStatementHandling.Script)
|
|
|
+ .InitialStepMode(StepMode.Into));
|
|
|
+
|
|
|
+ bool didBreak = false;
|
|
|
+ int stepCount = 0;
|
|
|
+ engine.DebugHandler.Break += (sender, info) =>
|
|
|
+ {
|
|
|
+ didBreak = true;
|
|
|
+ return StepMode.None;
|
|
|
+ };
|
|
|
+
|
|
|
+ engine.DebugHandler.Step += (sender, info) =>
|
|
|
+ {
|
|
|
+ stepCount++;
|
|
|
+ return StepMode.Into;
|
|
|
+ };
|
|
|
+
|
|
|
+ engine.Execute(script);
|
|
|
+ Assert.Equal(3, stepCount);
|
|
|
+ Assert.False(didBreak);
|
|
|
+ }
|
|
|
+
|
|
|
+ [Fact]
|
|
|
+ public void BreakPointDoesNotTriggerBreakWhenStepping()
|
|
|
+ {
|
|
|
+ string script = @"
|
|
|
+'first breakpoint';
|
|
|
+'dummy';
|
|
|
+'second breakpoint';";
|
|
|
+
|
|
|
+ var engine = new Engine(options => options
|
|
|
+ .DebugMode()
|
|
|
+ .InitialStepMode(StepMode.Into));
|
|
|
+
|
|
|
+ bool didStep = true;
|
|
|
+ bool didBreak = true;
|
|
|
+
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint(2, 0));
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint(4, 0));
|
|
|
+
|
|
|
+ engine.DebugHandler.Break += (sender, info) =>
|
|
|
+ {
|
|
|
+ didBreak = true;
|
|
|
+ // first breakpoint shouldn't cause us to get here, because we're stepping,
|
|
|
+ // but when we reach the second, we're running:
|
|
|
+ Assert.True(TestHelpers.ReachedLiteral(info, "second breakpoint"));
|
|
|
+ return StepMode.None;
|
|
|
+ };
|
|
|
+
|
|
|
+ engine.DebugHandler.Step += (sender, info) =>
|
|
|
+ {
|
|
|
+ didStep = true;
|
|
|
+ if (TestHelpers.ReachedLiteral(info, "first breakpoint"))
|
|
|
+ {
|
|
|
+ // Run from here
|
|
|
+ return StepMode.None;
|
|
|
+ }
|
|
|
+ return StepMode.Into;
|
|
|
+ };
|
|
|
+
|
|
|
+ engine.Execute(script);
|
|
|
+
|
|
|
+ Assert.True(didStep);
|
|
|
+ Assert.True(didBreak);
|
|
|
+ }
|
|
|
+
|
|
|
[Fact(Skip = "Non-source breakpoint is triggered before Statement, while debugger statement is now triggered by ExecuteInternal")]
|
|
|
public void DebuggerStatementAndBreakpointTriggerSingleBreak()
|
|
|
{
|
|
@@ -110,7 +290,7 @@ debugger;
|
|
|
.DebugMode()
|
|
|
.DebuggerStatementHandling(DebuggerStatementHandling.Script));
|
|
|
|
|
|
- engine.DebugHandler.BreakPoints.Add(new BreakPoint(2, 0));
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint(2, 0));
|
|
|
|
|
|
int breakTriggered = 0;
|
|
|
engine.DebugHandler.Break += (sender, info) =>
|
|
@@ -138,8 +318,8 @@ test();";
|
|
|
|
|
|
var engine = new Engine(options => options.DebugMode());
|
|
|
|
|
|
- engine.DebugHandler.BreakPoints.Add(new BreakPoint(4, 0));
|
|
|
- engine.DebugHandler.BreakPoints.Add(new BreakPoint(6, 0));
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint(4, 0));
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint(6, 0));
|
|
|
|
|
|
int step = 0;
|
|
|
engine.DebugHandler.Break += (sender, info) =>
|
|
@@ -160,5 +340,109 @@ test();";
|
|
|
|
|
|
Assert.Equal(2, step);
|
|
|
}
|
|
|
+
|
|
|
+ [Fact]
|
|
|
+ public void ErrorInConditionalBreakpointLeavesCallStackAlone()
|
|
|
+ {
|
|
|
+ string script = @"
|
|
|
+function foo()
|
|
|
+{
|
|
|
+let x = 0;
|
|
|
+'before breakpoint';
|
|
|
+'breakpoint 1 here';
|
|
|
+'breakpoint 2 here';
|
|
|
+'after breakpoint';
|
|
|
+}
|
|
|
+
|
|
|
+foo();
|
|
|
+";
|
|
|
+ var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.Into));
|
|
|
+
|
|
|
+ int stepsReached = 0;
|
|
|
+ int breakpointsReached = 0;
|
|
|
+
|
|
|
+ // This breakpoint will be hit:
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint(6, 0, "x == 0"));
|
|
|
+ // This condition is an error (y is not defined). DebugHandler will
|
|
|
+ // treat it as an unmatched breakpoint:
|
|
|
+ engine.DebugHandler.BreakPoints.Set(new BreakPoint(7, 0, "y == 0"));
|
|
|
+
|
|
|
+ engine.DebugHandler.Step += (sender, info) =>
|
|
|
+ {
|
|
|
+ if (info.ReachedLiteral("before breakpoint"))
|
|
|
+ {
|
|
|
+ Assert.Equal(1, engine.CallStack.Count);
|
|
|
+ stepsReached++;
|
|
|
+ return StepMode.None;
|
|
|
+ }
|
|
|
+ else if (info.ReachedLiteral("after breakpoint"))
|
|
|
+ {
|
|
|
+ Assert.Equal(1, engine.CallStack.Count);
|
|
|
+ stepsReached++;
|
|
|
+ return StepMode.None;
|
|
|
+ }
|
|
|
+ return StepMode.Into;
|
|
|
+ };
|
|
|
+
|
|
|
+ engine.DebugHandler.Break += (sender, info) =>
|
|
|
+ {
|
|
|
+ breakpointsReached++;
|
|
|
+ return StepMode.Into;
|
|
|
+ };
|
|
|
+
|
|
|
+ engine.Execute(script);
|
|
|
+
|
|
|
+ Assert.Equal(1, breakpointsReached);
|
|
|
+ Assert.Equal(2, stepsReached);
|
|
|
+ }
|
|
|
+
|
|
|
+ private class SimpleHitConditionBreakPoint : BreakPoint
|
|
|
+ {
|
|
|
+ public SimpleHitConditionBreakPoint(int line, int column, string condition = null,
|
|
|
+ int? hitCondition = null) : base(line, column, condition)
|
|
|
+ {
|
|
|
+ HitCondition = hitCondition;
|
|
|
+ }
|
|
|
+
|
|
|
+ public int HitCount { get; set; }
|
|
|
+ public int? HitCondition { get; set; }
|
|
|
+ }
|
|
|
+
|
|
|
+ [Fact]
|
|
|
+ public void BreakPointCanBeExtended()
|
|
|
+ {
|
|
|
+ // More of a documentation than a required test, this shows the usefulness of BreakPoint being
|
|
|
+ // extensible - as a test, at least it ensures that it is.
|
|
|
+ var script = @"
|
|
|
+for (let i = 0; i < 10; i++)
|
|
|
+{
|
|
|
+ 'breakpoint';
|
|
|
+}
|
|
|
+";
|
|
|
+ var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.None));
|
|
|
+
|
|
|
+ engine.DebugHandler.BreakPoints.Set(
|
|
|
+ new SimpleHitConditionBreakPoint(4, 4, condition: null, hitCondition: 5));
|
|
|
+
|
|
|
+ int numberOfBreaks = 0;
|
|
|
+ engine.DebugHandler.Break += (sender, info) =>
|
|
|
+ {
|
|
|
+ Assert.True(info.ReachedLiteral("breakpoint"));
|
|
|
+ var extendedBreakPoint = Assert.IsType<SimpleHitConditionBreakPoint>(info.BreakPoint);
|
|
|
+ extendedBreakPoint.HitCount++;
|
|
|
+ if (extendedBreakPoint.HitCount == extendedBreakPoint.HitCondition)
|
|
|
+ {
|
|
|
+ // Here is where we would normally pause the execution.
|
|
|
+ // the breakpoint is hit for the fifth time, when i is 4 (off by one)
|
|
|
+ Assert.Equal(4, info.CurrentScopeChain[0].GetBindingValue("i").AsInteger());
|
|
|
+ numberOfBreaks++;
|
|
|
+ }
|
|
|
+ return StepMode.None;
|
|
|
+ };
|
|
|
+
|
|
|
+ engine.Execute(script);
|
|
|
+
|
|
|
+ Assert.Equal(1, numberOfBreaks);
|
|
|
+ }
|
|
|
}
|
|
|
}
|