BreakPointTests.cs 14 KB

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