testDebuggerBreakpointManager.cxx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  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. return true;
  48. }
  49. static bool testHandleBreakpointRequestAfterFileIsLoaded()
  50. {
  51. // Arrange
  52. DebuggerTestHelper helper;
  53. std::atomic<bool> notExpectBreakpointEvents(true);
  54. helper.Client->registerHandler([&](const dap::BreakpointEvent&) {
  55. notExpectBreakpointEvents.store(false);
  56. });
  57. cmDebugger::cmDebuggerBreakpointManager breakpointManager(
  58. helper.Debugger.get());
  59. helper.bind();
  60. std::string sourcePath = "C:/CMakeLists.txt";
  61. std::vector<cmListFileFunction> functions = helper.CreateListFileFunctions(
  62. "# Comment1\nset(var1 foo)\n# Comment2\nset(var2\nbar)\n",
  63. sourcePath.c_str());
  64. breakpointManager.SourceFileLoaded(sourcePath, functions);
  65. dap::SetBreakpointsRequest setBreakpointRequest;
  66. setBreakpointRequest.source.path = sourcePath;
  67. dap::array<dap::SourceBreakpoint> sourceBreakpoints(5);
  68. sourceBreakpoints[0].line = 1;
  69. sourceBreakpoints[1].line = 2;
  70. sourceBreakpoints[2].line = 3;
  71. sourceBreakpoints[3].line = 4;
  72. sourceBreakpoints[4].line = 5;
  73. setBreakpointRequest.breakpoints = sourceBreakpoints;
  74. // Act
  75. auto got = helper.Client->send(setBreakpointRequest).get();
  76. // Assert
  77. auto& response = got.response;
  78. ASSERT_TRUE(!got.error);
  79. ASSERT_TRUE(response.breakpoints.size() == sourceBreakpoints.size());
  80. // Line 1 is a comment. Move it to next valid function, which is line 2.
  81. ASSERT_BREAKPOINT(response.breakpoints[0], 0, 2, sourcePath, true);
  82. ASSERT_BREAKPOINT(response.breakpoints[1], 1, sourceBreakpoints[1].line,
  83. sourcePath, true);
  84. // Line 3 is a comment. Move it to next valid function, which is line 4.
  85. ASSERT_BREAKPOINT(response.breakpoints[2], 2, 4, sourcePath, true);
  86. ASSERT_BREAKPOINT(response.breakpoints[3], 3, sourceBreakpoints[3].line,
  87. sourcePath, true);
  88. // Line 5 is the 2nd part of line 4 function. No valid function after line 5,
  89. // show the breakpoint at line 4.
  90. ASSERT_BREAKPOINT(response.breakpoints[4], 4, sourceBreakpoints[3].line,
  91. sourcePath, true);
  92. ASSERT_TRUE(notExpectBreakpointEvents.load());
  93. return true;
  94. }
  95. static bool testSourceFileLoadedAfterHandleBreakpointRequest()
  96. {
  97. // Arrange
  98. DebuggerTestHelper helper;
  99. std::vector<dap::BreakpointEvent> breakpointEvents;
  100. std::atomic<int> remainingBreakpointEvents(5);
  101. std::promise<void> allBreakpointEventsReceivedPromise;
  102. std::future<void> allBreakpointEventsReceivedFuture =
  103. allBreakpointEventsReceivedPromise.get_future();
  104. helper.Client->registerHandler([&](const dap::BreakpointEvent& event) {
  105. breakpointEvents.emplace_back(event);
  106. if (--remainingBreakpointEvents == 0) {
  107. allBreakpointEventsReceivedPromise.set_value();
  108. }
  109. });
  110. cmDebugger::cmDebuggerBreakpointManager breakpointManager(
  111. helper.Debugger.get());
  112. helper.bind();
  113. dap::SetBreakpointsRequest setBreakpointRequest;
  114. std::string sourcePath = "C:/CMakeLists.txt";
  115. setBreakpointRequest.source.path = sourcePath;
  116. dap::array<dap::SourceBreakpoint> sourceBreakpoints(5);
  117. sourceBreakpoints[0].line = 1;
  118. sourceBreakpoints[1].line = 2;
  119. sourceBreakpoints[2].line = 3;
  120. sourceBreakpoints[3].line = 4;
  121. sourceBreakpoints[4].line = 5;
  122. setBreakpointRequest.breakpoints = sourceBreakpoints;
  123. std::vector<cmListFileFunction> functions = helper.CreateListFileFunctions(
  124. "# Comment1\nset(var1 foo)\n# Comment2\nset(var2\nbar)\n",
  125. sourcePath.c_str());
  126. auto got = helper.Client->send(setBreakpointRequest).get();
  127. // Act
  128. breakpointManager.SourceFileLoaded(sourcePath, functions);
  129. ASSERT_TRUE(allBreakpointEventsReceivedFuture.wait_for(
  130. std::chrono::seconds(10)) == std::future_status::ready);
  131. // Assert
  132. ASSERT_TRUE(breakpointEvents.size() > 0);
  133. // Line 1 is a comment. Move it to next valid function, which is line 2.
  134. ASSERT_BREAKPOINT(breakpointEvents[0].breakpoint, 0, 2, sourcePath, true);
  135. ASSERT_BREAKPOINT(breakpointEvents[1].breakpoint, 1,
  136. sourceBreakpoints[1].line, sourcePath, true);
  137. // Line 3 is a comment. Move it to next valid function, which is line 4.
  138. ASSERT_BREAKPOINT(breakpointEvents[2].breakpoint, 2, 4, sourcePath, true);
  139. ASSERT_BREAKPOINT(breakpointEvents[3].breakpoint, 3,
  140. sourceBreakpoints[3].line, sourcePath, true);
  141. // Line 5 is the 2nd part of line 4 function. No valid function after line 5,
  142. // show the breakpoint at line 4.
  143. ASSERT_BREAKPOINT(breakpointEvents[4].breakpoint, 4,
  144. sourceBreakpoints[3].line, sourcePath, true);
  145. return true;
  146. }
  147. int testDebuggerBreakpointManager(int, char*[])
  148. {
  149. return runTests(std::vector<std::function<bool()>>{
  150. testHandleBreakpointRequestBeforeFileIsLoaded,
  151. testHandleBreakpointRequestAfterFileIsLoaded,
  152. testSourceFileLoadedAfterHandleBreakpointRequest,
  153. });
  154. }