testDebuggerBreakpointManager.cxx 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include <atomic>
  4. #include <chrono>
  5. #include <functional>
  6. #include <future>
  7. #include <memory>
  8. #include <string>
  9. #include <vector>
  10. #include <cm3p/cppdap/future.h>
  11. #include <cm3p/cppdap/optional.h>
  12. #include <cm3p/cppdap/protocol.h>
  13. #include <cm3p/cppdap/session.h>
  14. #include <cm3p/cppdap/types.h>
  15. #include "cmDebuggerBreakpointManager.h"
  16. #include "cmDebuggerSourceBreakpoint.h" // IWYU pragma: keep
  17. #include "cmListFileCache.h"
  18. #include "testCommon.h"
  19. #include "testDebugger.h"
  20. static bool testHandleBreakpointRequestBeforeFileIsLoaded()
  21. {
  22. // Arrange
  23. DebuggerTestHelper helper;
  24. cmDebugger::cmDebuggerBreakpointManager breakpointManager(
  25. helper.Debugger.get());
  26. helper.bind();
  27. dap::SetBreakpointsRequest setBreakpointRequest;
  28. std::string sourcePath = "C:/CMakeLists.txt";
  29. setBreakpointRequest.source.path = sourcePath;
  30. dap::array<dap::SourceBreakpoint> sourceBreakpoints(3);
  31. sourceBreakpoints[0].line = 1;
  32. sourceBreakpoints[1].line = 2;
  33. sourceBreakpoints[2].line = 3;
  34. setBreakpointRequest.breakpoints = sourceBreakpoints;
  35. // Act
  36. auto got = helper.Client->send(setBreakpointRequest).get();
  37. // Assert
  38. auto& response = got.response;
  39. ASSERT_TRUE(!got.error);
  40. ASSERT_TRUE(response.breakpoints.size() == sourceBreakpoints.size());
  41. ASSERT_BREAKPOINT(response.breakpoints[0], 0, sourceBreakpoints[0].line,
  42. sourcePath, false);
  43. ASSERT_BREAKPOINT(response.breakpoints[1], 1, sourceBreakpoints[1].line,
  44. sourcePath, false);
  45. ASSERT_BREAKPOINT(response.breakpoints[2], 2, sourceBreakpoints[2].line,
  46. sourcePath, false);
  47. ASSERT_TRUE(breakpointManager.GetBreakpointCount() == 3);
  48. // setBreakpoints should override any existing breakpoints
  49. setBreakpointRequest.breakpoints.value().clear();
  50. helper.Client->send(setBreakpointRequest).get();
  51. ASSERT_TRUE(breakpointManager.GetBreakpointCount() == 0);
  52. return true;
  53. }
  54. static bool testHandleBreakpointRequestAfterFileIsLoaded()
  55. {
  56. // Arrange
  57. DebuggerTestHelper helper;
  58. std::atomic<bool> notExpectBreakpointEvents(true);
  59. helper.Client->registerHandler([&](const dap::BreakpointEvent&) {
  60. notExpectBreakpointEvents.store(false);
  61. });
  62. cmDebugger::cmDebuggerBreakpointManager breakpointManager(
  63. helper.Debugger.get());
  64. helper.bind();
  65. std::string sourcePath = "C:/CMakeLists.txt";
  66. std::vector<cmListFileFunction> functions = helper.CreateListFileFunctions(
  67. "# Comment1\nset(var1 foo)\n# Comment2\nset(var2\nbar)\n",
  68. sourcePath.c_str());
  69. breakpointManager.SourceFileLoaded(sourcePath, functions);
  70. dap::SetBreakpointsRequest setBreakpointRequest;
  71. setBreakpointRequest.source.path = sourcePath;
  72. dap::array<dap::SourceBreakpoint> sourceBreakpoints(5);
  73. sourceBreakpoints[0].line = 1;
  74. sourceBreakpoints[1].line = 2;
  75. sourceBreakpoints[2].line = 3;
  76. sourceBreakpoints[3].line = 4;
  77. sourceBreakpoints[4].line = 5;
  78. setBreakpointRequest.breakpoints = sourceBreakpoints;
  79. // Act
  80. auto got = helper.Client->send(setBreakpointRequest).get();
  81. // Assert
  82. auto& response = got.response;
  83. ASSERT_TRUE(!got.error);
  84. ASSERT_TRUE(response.breakpoints.size() == sourceBreakpoints.size());
  85. // Line 1 is a comment. Move it to next valid function, which is line 2.
  86. ASSERT_BREAKPOINT(response.breakpoints[0], 0, 2, sourcePath, true);
  87. ASSERT_BREAKPOINT(response.breakpoints[1], 1, sourceBreakpoints[1].line,
  88. sourcePath, true);
  89. // Line 3 is a comment. Move it to next valid function, which is line 4.
  90. ASSERT_BREAKPOINT(response.breakpoints[2], 2, 4, sourcePath, true);
  91. ASSERT_BREAKPOINT(response.breakpoints[3], 3, sourceBreakpoints[3].line,
  92. sourcePath, true);
  93. // Line 5 is the 2nd part of line 4 function. No valid function after line 5,
  94. // show the breakpoint at line 4.
  95. ASSERT_BREAKPOINT(response.breakpoints[4], 4, sourceBreakpoints[3].line,
  96. sourcePath, true);
  97. ASSERT_TRUE(notExpectBreakpointEvents.load());
  98. ASSERT_TRUE(breakpointManager.GetBreakpointCount() == 5);
  99. // setBreakpoints should override any existing breakpoints
  100. setBreakpointRequest.breakpoints.value().clear();
  101. helper.Client->send(setBreakpointRequest).get();
  102. ASSERT_TRUE(breakpointManager.GetBreakpointCount() == 0);
  103. return true;
  104. }
  105. static bool testSourceFileLoadedAfterHandleBreakpointRequest()
  106. {
  107. // Arrange
  108. DebuggerTestHelper helper;
  109. std::vector<dap::BreakpointEvent> breakpointEvents;
  110. std::atomic<int> remainingBreakpointEvents(5);
  111. std::promise<void> allBreakpointEventsReceivedPromise;
  112. std::future<void> allBreakpointEventsReceivedFuture =
  113. allBreakpointEventsReceivedPromise.get_future();
  114. helper.Client->registerHandler([&](const dap::BreakpointEvent& event) {
  115. breakpointEvents.emplace_back(event);
  116. if (--remainingBreakpointEvents == 0) {
  117. allBreakpointEventsReceivedPromise.set_value();
  118. }
  119. });
  120. cmDebugger::cmDebuggerBreakpointManager breakpointManager(
  121. helper.Debugger.get());
  122. helper.bind();
  123. dap::SetBreakpointsRequest setBreakpointRequest;
  124. std::string sourcePath = "C:/CMakeLists.txt";
  125. setBreakpointRequest.source.path = sourcePath;
  126. dap::array<dap::SourceBreakpoint> sourceBreakpoints(5);
  127. sourceBreakpoints[0].line = 1;
  128. sourceBreakpoints[1].line = 2;
  129. sourceBreakpoints[2].line = 3;
  130. sourceBreakpoints[3].line = 4;
  131. sourceBreakpoints[4].line = 5;
  132. setBreakpointRequest.breakpoints = sourceBreakpoints;
  133. std::vector<cmListFileFunction> functions = helper.CreateListFileFunctions(
  134. "# Comment1\nset(var1 foo)\n# Comment2\nset(var2\nbar)\n",
  135. sourcePath.c_str());
  136. auto got = helper.Client->send(setBreakpointRequest).get();
  137. // Act
  138. breakpointManager.SourceFileLoaded(sourcePath, functions);
  139. ASSERT_TRUE(allBreakpointEventsReceivedFuture.wait_for(
  140. std::chrono::seconds(10)) == std::future_status::ready);
  141. // Assert
  142. ASSERT_TRUE(breakpointEvents.size() > 0);
  143. // Line 1 is a comment. Move it to next valid function, which is line 2.
  144. ASSERT_BREAKPOINT(breakpointEvents[0].breakpoint, 0, 2, sourcePath, true);
  145. ASSERT_BREAKPOINT(breakpointEvents[1].breakpoint, 1,
  146. sourceBreakpoints[1].line, sourcePath, true);
  147. // Line 3 is a comment. Move it to next valid function, which is line 4.
  148. ASSERT_BREAKPOINT(breakpointEvents[2].breakpoint, 2, 4, sourcePath, true);
  149. ASSERT_BREAKPOINT(breakpointEvents[3].breakpoint, 3,
  150. sourceBreakpoints[3].line, sourcePath, true);
  151. // Line 5 is the 2nd part of line 4 function. No valid function after line 5,
  152. // show the breakpoint at line 4.
  153. ASSERT_BREAKPOINT(breakpointEvents[4].breakpoint, 4,
  154. sourceBreakpoints[3].line, sourcePath, true);
  155. return true;
  156. }
  157. int testDebuggerBreakpointManager(int, char*[])
  158. {
  159. return runTests(std::vector<std::function<bool()>>{
  160. testHandleBreakpointRequestBeforeFileIsLoaded,
  161. testHandleBreakpointRequestAfterFileIsLoaded,
  162. testSourceFileLoadedAfterHandleBreakpointRequest,
  163. });
  164. }