testDebuggerAdapter.cxx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file LICENSE.rst or https://cmake.org/licensing for details. */
  3. #include <chrono>
  4. #include <cstdio>
  5. #include <future>
  6. #include <memory>
  7. #include <string>
  8. #include <cm3p/cppdap/future.h>
  9. #include <cm3p/cppdap/io.h>
  10. #include <cm3p/cppdap/optional.h>
  11. #include <cm3p/cppdap/protocol.h>
  12. #include <cm3p/cppdap/session.h>
  13. #include <cm3p/cppdap/types.h>
  14. #include "cmDebuggerAdapter.h"
  15. #include "cmDebuggerProtocol.h"
  16. #include "cmVersionConfig.h"
  17. #include "testCommon.h"
  18. #include "testDebugger.h"
  19. class DebuggerLocalConnection : public cmDebugger::cmDebuggerConnection
  20. {
  21. public:
  22. DebuggerLocalConnection()
  23. : ClientToDebugger(dap::pipe())
  24. , DebuggerToClient(dap::pipe())
  25. {
  26. }
  27. bool StartListening(std::string& errorMessage) override
  28. {
  29. errorMessage = "";
  30. return true;
  31. }
  32. void WaitForConnection() override {}
  33. std::shared_ptr<dap::Reader> GetReader() override
  34. {
  35. return ClientToDebugger;
  36. };
  37. std::shared_ptr<dap::Writer> GetWriter() override
  38. {
  39. return DebuggerToClient;
  40. }
  41. std::shared_ptr<dap::ReaderWriter> ClientToDebugger;
  42. std::shared_ptr<dap::ReaderWriter> DebuggerToClient;
  43. };
  44. bool runTest(std::function<bool(dap::Session&)> onThreadExitedEvent)
  45. {
  46. std::promise<bool> debuggerAdapterInitializedPromise;
  47. std::future<bool> debuggerAdapterInitializedFuture =
  48. debuggerAdapterInitializedPromise.get_future();
  49. std::promise<bool> initializedEventReceivedPromise;
  50. std::future<bool> initializedEventReceivedFuture =
  51. initializedEventReceivedPromise.get_future();
  52. std::promise<bool> exitedEventReceivedPromise;
  53. std::future<bool> exitedEventReceivedFuture =
  54. exitedEventReceivedPromise.get_future();
  55. std::promise<bool> terminatedEventReceivedPromise;
  56. std::future<bool> terminatedEventReceivedFuture =
  57. terminatedEventReceivedPromise.get_future();
  58. std::promise<bool> threadStartedPromise;
  59. std::future<bool> threadStartedFuture = threadStartedPromise.get_future();
  60. std::promise<bool> threadExitedPromise;
  61. std::future<bool> threadExitedFuture = threadExitedPromise.get_future();
  62. std::promise<bool> disconnectResponseReceivedPromise;
  63. std::future<bool> disconnectResponseReceivedFuture =
  64. disconnectResponseReceivedPromise.get_future();
  65. auto futureTimeout = std::chrono::seconds(60);
  66. auto connection = std::make_shared<DebuggerLocalConnection>();
  67. std::unique_ptr<dap::Session> client = dap::Session::create();
  68. client->registerHandler([&](dap::InitializedEvent /*unused*/) {
  69. initializedEventReceivedPromise.set_value(true);
  70. });
  71. client->registerHandler([&](dap::ExitedEvent /*unused*/) {
  72. exitedEventReceivedPromise.set_value(true);
  73. });
  74. client->registerHandler([&](dap::TerminatedEvent const& /*unused*/) {
  75. terminatedEventReceivedPromise.set_value(true);
  76. });
  77. client->registerHandler([&](dap::ThreadEvent const& e) {
  78. if (e.reason == "started") {
  79. threadStartedPromise.set_value(true);
  80. } else if (e.reason == "exited") {
  81. threadExitedPromise.set_value(true);
  82. }
  83. });
  84. client->bind(connection->DebuggerToClient, connection->ClientToDebugger);
  85. ScopedThread debuggerThread([&]() -> int {
  86. std::shared_ptr<cmDebugger::cmDebuggerAdapter> debuggerAdapter =
  87. std::make_shared<cmDebugger::cmDebuggerAdapter>(
  88. connection, dap::file(stdout, false));
  89. debuggerAdapterInitializedPromise.set_value(true);
  90. debuggerAdapter->ReportExitCode(0);
  91. // Ensure the disconnectResponse has been received before
  92. // destructing debuggerAdapter.
  93. ASSERT_TRUE(disconnectResponseReceivedFuture.wait_for(futureTimeout) ==
  94. std::future_status::ready);
  95. return 0;
  96. });
  97. dap::CMakeInitializeRequest initializeRequest;
  98. auto initializeResponse = client->send(initializeRequest).get();
  99. ASSERT_TRUE(initializeResponse.response.cmakeVersion.full == CMake_VERSION);
  100. ASSERT_TRUE(initializeResponse.response.cmakeVersion.major ==
  101. CMake_VERSION_MAJOR);
  102. ASSERT_TRUE(initializeResponse.response.cmakeVersion.minor ==
  103. CMake_VERSION_MINOR);
  104. ASSERT_TRUE(initializeResponse.response.cmakeVersion.patch ==
  105. CMake_VERSION_PATCH);
  106. ASSERT_TRUE(initializeResponse.response.supportsExceptionInfoRequest);
  107. ASSERT_TRUE(
  108. initializeResponse.response.exceptionBreakpointFilters.has_value());
  109. ASSERT_TRUE(initializeResponse.response.supportsValueFormattingOptions);
  110. dap::LaunchRequest launchRequest;
  111. auto launchResponse = client->send(launchRequest).get();
  112. ASSERT_TRUE(!launchResponse.error);
  113. dap::ConfigurationDoneRequest configurationDoneRequest;
  114. auto configurationDoneResponse =
  115. client->send(configurationDoneRequest).get();
  116. ASSERT_TRUE(!configurationDoneResponse.error);
  117. ASSERT_TRUE(debuggerAdapterInitializedFuture.wait_for(futureTimeout) ==
  118. std::future_status::ready);
  119. ASSERT_TRUE(initializedEventReceivedFuture.wait_for(futureTimeout) ==
  120. std::future_status::ready);
  121. ASSERT_TRUE(threadStartedFuture.wait_for(futureTimeout) ==
  122. std::future_status::ready);
  123. ASSERT_TRUE(threadExitedFuture.wait_for(futureTimeout) ==
  124. std::future_status::ready);
  125. if (onThreadExitedEvent) {
  126. ASSERT_TRUE(onThreadExitedEvent(*client));
  127. }
  128. ASSERT_TRUE(exitedEventReceivedFuture.wait_for(futureTimeout) ==
  129. std::future_status::ready);
  130. ASSERT_TRUE(terminatedEventReceivedFuture.wait_for(futureTimeout) ==
  131. std::future_status::ready);
  132. dap::DisconnectRequest disconnectRequest;
  133. auto disconnectResponse = client->send(disconnectRequest).get();
  134. disconnectResponseReceivedPromise.set_value(true);
  135. ASSERT_TRUE(!disconnectResponse.error);
  136. return true;
  137. }
  138. bool testBasicProtocol()
  139. {
  140. return runTest(nullptr);
  141. }
  142. bool testThreadsRequestAfterThreadExitedEvent()
  143. {
  144. return runTest([](dap::Session& session) -> bool {
  145. // Try requesting threads again after receiving the thread exited event.
  146. // Some clients do this to ensure that their thread list is up-to-date.
  147. dap::ThreadsRequest threadsRequest;
  148. auto threadsResponse = session.send(threadsRequest).get();
  149. ASSERT_TRUE(!threadsResponse.error);
  150. // CMake only has one DAP thread. Once that thread exits, there should be
  151. // no threads left.
  152. ASSERT_TRUE(threadsResponse.response.threads.empty());
  153. return true;
  154. });
  155. }
  156. int testDebuggerAdapter(int, char*[])
  157. {
  158. return runTests(
  159. { testBasicProtocol, testThreadsRequestAfterThreadExitedEvent });
  160. }