BreakPointTests.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. using Esprima;
  2. using Jint.Runtime.Debugger;
  3. using Xunit;
  4. namespace Jint.Tests.Runtime.Debugger
  5. {
  6. public class BreakPointTests
  7. {
  8. [Fact]
  9. public void BreakLocationsCompareEqualityByValue()
  10. {
  11. var loc1 = new BreakLocation(42, 23);
  12. var loc2 = new BreakLocation(42, 23);
  13. var loc3 = new BreakLocation(17, 7);
  14. Assert.Equal(loc1, loc2);
  15. Assert.True(loc1 == loc2);
  16. Assert.True(loc2 != loc3);
  17. Assert.False(loc1 != loc2);
  18. Assert.False(loc2 == loc3);
  19. }
  20. [Fact]
  21. public void BreakLocationsWithSourceCompareEqualityByValue()
  22. {
  23. var loc1 = new BreakLocation("script1", 42, 23);
  24. var loc2 = new BreakLocation("script1", 42, 23);
  25. var loc3 = new BreakLocation("script2", 42, 23);
  26. Assert.Equal(loc1, loc2);
  27. Assert.True(loc1 == loc2);
  28. Assert.True(loc2 != loc3);
  29. Assert.False(loc1 != loc2);
  30. Assert.False(loc2 == loc3);
  31. }
  32. [Fact]
  33. public void BreakLocationsOptionalSourceEqualityComparer()
  34. {
  35. var script1 = new BreakLocation("script1", 42, 23);
  36. var script2 = new BreakLocation("script2", 42, 23);
  37. var script2b = new BreakLocation("script2", 44, 23);
  38. var any = new BreakLocation(null, 42, 23);
  39. var comparer = new OptionalSourceBreakLocationEqualityComparer();
  40. Assert.True(comparer.Equals(script1, any));
  41. Assert.True(comparer.Equals(script2, any));
  42. Assert.False(comparer.Equals(script1, script2));
  43. Assert.False(comparer.Equals(script2, script2b));
  44. Assert.Equal(comparer.GetHashCode(script1), comparer.GetHashCode(any));
  45. Assert.Equal(comparer.GetHashCode(script1), comparer.GetHashCode(script2));
  46. Assert.NotEqual(comparer.GetHashCode(script2), comparer.GetHashCode(script2b));
  47. }
  48. [Fact]
  49. public void BreakPointReplacesPreviousBreakPoint()
  50. {
  51. var engine = new Engine(options => options.DebugMode());
  52. engine.DebugHandler.BreakPoints.Set(new BreakPoint(4, 5, "i === 1"));
  53. Assert.Collection(engine.DebugHandler.BreakPoints,
  54. breakPoint =>
  55. {
  56. Assert.Equal(4, breakPoint.Location.Line);
  57. Assert.Equal(5, breakPoint.Location.Column);
  58. Assert.Equal("i === 1", breakPoint.Condition);
  59. });
  60. engine.DebugHandler.BreakPoints.Set(new BreakPoint(4, 5));
  61. Assert.Collection(engine.DebugHandler.BreakPoints,
  62. breakPoint =>
  63. {
  64. Assert.Equal(4, breakPoint.Location.Line);
  65. Assert.Equal(5, breakPoint.Location.Column);
  66. Assert.Equal(null, breakPoint.Condition);
  67. });
  68. }
  69. [Fact]
  70. public void BreakPointRemovesBasedOnLocationEquality()
  71. {
  72. var engine = new Engine(options => options.DebugMode());
  73. engine.DebugHandler.BreakPoints.Set(new BreakPoint(4, 5, "i === 1"));
  74. engine.DebugHandler.BreakPoints.Set(new BreakPoint(5, 6, "j === 2"));
  75. engine.DebugHandler.BreakPoints.Set(new BreakPoint(10, 7, "x > 5"));
  76. Assert.Equal(3, engine.DebugHandler.BreakPoints.Count);
  77. engine.DebugHandler.BreakPoints.RemoveAt(new BreakLocation(null, 4, 5));
  78. engine.DebugHandler.BreakPoints.RemoveAt(new BreakLocation(null, 10, 7));
  79. Assert.Collection(engine.DebugHandler.BreakPoints,
  80. breakPoint =>
  81. {
  82. Assert.Equal(5, breakPoint.Location.Line);
  83. Assert.Equal(6, breakPoint.Location.Column);
  84. Assert.Equal("j === 2", breakPoint.Condition);
  85. });
  86. }
  87. [Fact]
  88. public void BreakPointContainsBasedOnLocationEquality()
  89. {
  90. var engine = new Engine(options => options.DebugMode());
  91. engine.DebugHandler.BreakPoints.Set(new BreakPoint(4, 5, "i === 1"));
  92. engine.DebugHandler.BreakPoints.Set(new BreakPoint(5, 6, "j === 2"));
  93. engine.DebugHandler.BreakPoints.Set(new BreakPoint(10, 7, "x > 5"));
  94. Assert.True(engine.DebugHandler.BreakPoints.Contains(new BreakLocation(null, 5, 6)));
  95. Assert.False(engine.DebugHandler.BreakPoints.Contains(new BreakLocation(null, 8, 9)));
  96. }
  97. [Fact]
  98. public void BreakPointBreaksAtPosition()
  99. {
  100. string script = @"let x = 1, y = 2;
  101. if (x === 1)
  102. {
  103. x++; y *= 2;
  104. }";
  105. var engine = new Engine(options => options.DebugMode());
  106. bool didBreak = false;
  107. engine.DebugHandler.Break += (sender, info) =>
  108. {
  109. Assert.Equal(4, info.Location.Start.Line);
  110. Assert.Equal(5, info.Location.Start.Column);
  111. didBreak = true;
  112. return StepMode.None;
  113. };
  114. engine.DebugHandler.BreakPoints.Set(new BreakPoint(4, 5));
  115. engine.Execute(script);
  116. Assert.True(didBreak);
  117. }
  118. [Fact]
  119. public void BreakPointBreaksInCorrectSource()
  120. {
  121. string script1 = @"let x = 1, y = 2;
  122. if (x === 1)
  123. {
  124. x++; y *= 2;
  125. }";
  126. string script2 = @"function test(x)
  127. {
  128. return x + 2;
  129. }";
  130. string script3 = @"const z = 3;
  131. test(z);";
  132. var engine = new Engine(options => { options.DebugMode(); });
  133. engine.DebugHandler.BreakPoints.Set(new BreakPoint("script2", 3, 0));
  134. bool didBreak = false;
  135. engine.DebugHandler.Break += (sender, info) =>
  136. {
  137. Assert.Equal("script2", info.Location.Source);
  138. Assert.Equal(3, info.Location.Start.Line);
  139. Assert.Equal(0, info.Location.Start.Column);
  140. didBreak = true;
  141. return StepMode.None;
  142. };
  143. // We need to specify the source to the parser.
  144. // And we need locations too (Jint specifies that in its default options)
  145. engine.Execute(script1, new ParserOptions("script1"));
  146. Assert.False(didBreak);
  147. engine.Execute(script2, new ParserOptions("script2"));
  148. Assert.False(didBreak);
  149. // Note that it's actually script3 that executes the function in script2
  150. // and triggers the breakpoint
  151. engine.Execute(script3, new ParserOptions("script3"));
  152. Assert.True(didBreak);
  153. }
  154. [Fact]
  155. public void DebuggerStatementTriggersBreak()
  156. {
  157. string script = @"'dummy';
  158. debugger;
  159. 'dummy';";
  160. var engine = new Engine(options => options
  161. .DebugMode()
  162. .DebuggerStatementHandling(DebuggerStatementHandling.Script));
  163. bool didBreak = false;
  164. engine.DebugHandler.Break += (sender, info) =>
  165. {
  166. didBreak = true;
  167. return StepMode.None;
  168. };
  169. engine.Execute(script);
  170. Assert.True(didBreak);
  171. }
  172. [Fact]
  173. public void DebuggerStatementDoesNotTriggerBreakWhenStepping()
  174. {
  175. string script = @"'dummy';
  176. debugger;
  177. 'dummy';";
  178. var engine = new Engine(options => options
  179. .DebugMode()
  180. .DebuggerStatementHandling(DebuggerStatementHandling.Script)
  181. .InitialStepMode(StepMode.Into));
  182. bool didBreak = false;
  183. int stepCount = 0;
  184. engine.DebugHandler.Break += (sender, info) =>
  185. {
  186. didBreak = true;
  187. return StepMode.None;
  188. };
  189. engine.DebugHandler.Step += (sender, info) =>
  190. {
  191. stepCount++;
  192. return StepMode.Into;
  193. };
  194. engine.Execute(script);
  195. Assert.Equal(3, stepCount);
  196. Assert.False(didBreak);
  197. }
  198. [Fact]
  199. public void BreakPointDoesNotTriggerBreakWhenStepping()
  200. {
  201. string script = @"
  202. 'first breakpoint';
  203. 'dummy';
  204. 'second breakpoint';";
  205. var engine = new Engine(options => options
  206. .DebugMode()
  207. .InitialStepMode(StepMode.Into));
  208. bool didStep = true;
  209. bool didBreak = true;
  210. engine.DebugHandler.BreakPoints.Set(new BreakPoint(2, 0));
  211. engine.DebugHandler.BreakPoints.Set(new BreakPoint(4, 0));
  212. engine.DebugHandler.Break += (sender, info) =>
  213. {
  214. didBreak = true;
  215. // first breakpoint shouldn't cause us to get here, because we're stepping,
  216. // but when we reach the second, we're running:
  217. Assert.True(TestHelpers.ReachedLiteral(info, "second breakpoint"));
  218. return StepMode.None;
  219. };
  220. engine.DebugHandler.Step += (sender, info) =>
  221. {
  222. didStep = true;
  223. if (TestHelpers.ReachedLiteral(info, "first breakpoint"))
  224. {
  225. // Run from here
  226. return StepMode.None;
  227. }
  228. return StepMode.Into;
  229. };
  230. engine.Execute(script);
  231. Assert.True(didStep);
  232. Assert.True(didBreak);
  233. }
  234. [Fact(Skip = "Non-source breakpoint is triggered before Statement, while debugger statement is now triggered by ExecuteInternal")]
  235. public void DebuggerStatementAndBreakpointTriggerSingleBreak()
  236. {
  237. string script = @"'dummy';
  238. debugger;
  239. 'dummy';";
  240. var engine = new Engine(options => options
  241. .DebugMode()
  242. .DebuggerStatementHandling(DebuggerStatementHandling.Script));
  243. engine.DebugHandler.BreakPoints.Set(new BreakPoint(2, 0));
  244. int breakTriggered = 0;
  245. engine.DebugHandler.Break += (sender, info) =>
  246. {
  247. breakTriggered++;
  248. return StepMode.None;
  249. };
  250. engine.Execute(script);
  251. Assert.Equal(1, breakTriggered);
  252. }
  253. [Fact]
  254. public void BreakpointOverridesStepOut()
  255. {
  256. string script = @"function test()
  257. {
  258. 'dummy';
  259. 'source';
  260. 'dummy';
  261. 'target';
  262. }
  263. test();";
  264. var engine = new Engine(options => options.DebugMode());
  265. engine.DebugHandler.BreakPoints.Set(new BreakPoint(4, 0));
  266. engine.DebugHandler.BreakPoints.Set(new BreakPoint(6, 0));
  267. int step = 0;
  268. engine.DebugHandler.Break += (sender, info) =>
  269. {
  270. step++;
  271. switch (step)
  272. {
  273. case 1:
  274. return StepMode.Out;
  275. case 2:
  276. Assert.True(info.ReachedLiteral("target"));
  277. break;
  278. }
  279. return StepMode.None;
  280. };
  281. engine.Execute(script);
  282. Assert.Equal(2, step);
  283. }
  284. [Fact]
  285. public void ErrorInConditionalBreakpointLeavesCallStackAlone()
  286. {
  287. string script = @"
  288. function foo()
  289. {
  290. let x = 0;
  291. 'before breakpoint';
  292. 'breakpoint 1 here';
  293. 'breakpoint 2 here';
  294. 'after breakpoint';
  295. }
  296. foo();
  297. ";
  298. var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.Into));
  299. int stepsReached = 0;
  300. int breakpointsReached = 0;
  301. // This breakpoint will be hit:
  302. engine.DebugHandler.BreakPoints.Set(new BreakPoint(6, 0, "x == 0"));
  303. // This condition is an error (y is not defined). DebugHandler will
  304. // treat it as an unmatched breakpoint:
  305. engine.DebugHandler.BreakPoints.Set(new BreakPoint(7, 0, "y == 0"));
  306. engine.DebugHandler.Step += (sender, info) =>
  307. {
  308. if (info.ReachedLiteral("before breakpoint"))
  309. {
  310. Assert.Equal(1, engine.CallStack.Count);
  311. stepsReached++;
  312. return StepMode.None;
  313. }
  314. else if (info.ReachedLiteral("after breakpoint"))
  315. {
  316. Assert.Equal(1, engine.CallStack.Count);
  317. stepsReached++;
  318. return StepMode.None;
  319. }
  320. return StepMode.Into;
  321. };
  322. engine.DebugHandler.Break += (sender, info) =>
  323. {
  324. breakpointsReached++;
  325. return StepMode.Into;
  326. };
  327. engine.Execute(script);
  328. Assert.Equal(1, breakpointsReached);
  329. Assert.Equal(2, stepsReached);
  330. }
  331. private class SimpleHitConditionBreakPoint : BreakPoint
  332. {
  333. public SimpleHitConditionBreakPoint(int line, int column, string condition = null,
  334. int? hitCondition = null) : base(line, column, condition)
  335. {
  336. HitCondition = hitCondition;
  337. }
  338. public int HitCount { get; set; }
  339. public int? HitCondition { get; set; }
  340. }
  341. [Fact]
  342. public void BreakPointCanBeExtended()
  343. {
  344. // More of a documentation than a required test, this shows the usefulness of BreakPoint being
  345. // extensible - as a test, at least it ensures that it is.
  346. var script = @"
  347. for (let i = 0; i < 10; i++)
  348. {
  349. 'breakpoint';
  350. }
  351. ";
  352. var engine = new Engine(options => options.DebugMode().InitialStepMode(StepMode.None));
  353. engine.DebugHandler.BreakPoints.Set(
  354. new SimpleHitConditionBreakPoint(4, 4, condition: null, hitCondition: 5));
  355. int numberOfBreaks = 0;
  356. engine.DebugHandler.Break += (sender, info) =>
  357. {
  358. Assert.True(info.ReachedLiteral("breakpoint"));
  359. var extendedBreakPoint = Assert.IsType<SimpleHitConditionBreakPoint>(info.BreakPoint);
  360. extendedBreakPoint.HitCount++;
  361. if (extendedBreakPoint.HitCount == extendedBreakPoint.HitCondition)
  362. {
  363. // Here is where we would normally pause the execution.
  364. // the breakpoint is hit for the fifth time, when i is 4 (off by one)
  365. Assert.Equal(4, info.CurrentScopeChain[0].GetBindingValue("i").AsInteger());
  366. numberOfBreaks++;
  367. }
  368. return StepMode.None;
  369. };
  370. engine.Execute(script);
  371. Assert.Equal(1, numberOfBreaks);
  372. }
  373. }
  374. }