BreakPointTests.cs 14 KB

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