summaryrefslogtreecommitdiffstats
path: root/Tests/CMakeLib/testDebuggerAdapter.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'Tests/CMakeLib/testDebuggerAdapter.cxx')
-rw-r--r--Tests/CMakeLib/testDebuggerAdapter.cxx173
1 files changed, 173 insertions, 0 deletions
diff --git a/Tests/CMakeLib/testDebuggerAdapter.cxx b/Tests/CMakeLib/testDebuggerAdapter.cxx
new file mode 100644
index 0000000..394986b
--- /dev/null
+++ b/Tests/CMakeLib/testDebuggerAdapter.cxx
@@ -0,0 +1,173 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include <chrono>
+#include <cstdio>
+#include <functional>
+#include <future>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <cm3p/cppdap/future.h>
+#include <cm3p/cppdap/io.h>
+#include <cm3p/cppdap/optional.h>
+#include <cm3p/cppdap/protocol.h>
+#include <cm3p/cppdap/session.h>
+#include <cm3p/cppdap/types.h>
+
+#include "cmDebuggerAdapter.h"
+#include "cmDebuggerProtocol.h"
+#include "cmVersionConfig.h"
+
+#include "testCommon.h"
+#include "testDebugger.h"
+
+class DebuggerLocalConnection : public cmDebugger::cmDebuggerConnection
+{
+public:
+ DebuggerLocalConnection()
+ : ClientToDebugger(dap::pipe())
+ , DebuggerToClient(dap::pipe())
+ {
+ }
+
+ bool StartListening(std::string& errorMessage) override
+ {
+ errorMessage = "";
+ return true;
+ }
+ void WaitForConnection() override {}
+
+ std::shared_ptr<dap::Reader> GetReader() override
+ {
+ return ClientToDebugger;
+ };
+
+ std::shared_ptr<dap::Writer> GetWriter() override
+ {
+ return DebuggerToClient;
+ }
+
+ std::shared_ptr<dap::ReaderWriter> ClientToDebugger;
+ std::shared_ptr<dap::ReaderWriter> DebuggerToClient;
+};
+
+bool testBasicProtocol()
+{
+ std::promise<bool> debuggerAdapterInitializedPromise;
+ std::future<bool> debuggerAdapterInitializedFuture =
+ debuggerAdapterInitializedPromise.get_future();
+
+ std::promise<bool> initializedEventReceivedPromise;
+ std::future<bool> initializedEventReceivedFuture =
+ initializedEventReceivedPromise.get_future();
+
+ std::promise<bool> exitedEventReceivedPromise;
+ std::future<bool> exitedEventReceivedFuture =
+ exitedEventReceivedPromise.get_future();
+
+ std::promise<bool> terminatedEventReceivedPromise;
+ std::future<bool> terminatedEventReceivedFuture =
+ terminatedEventReceivedPromise.get_future();
+
+ std::promise<bool> threadStartedPromise;
+ std::future<bool> threadStartedFuture = threadStartedPromise.get_future();
+
+ std::promise<bool> threadExitedPromise;
+ std::future<bool> threadExitedFuture = threadExitedPromise.get_future();
+
+ std::promise<bool> disconnectResponseReceivedPromise;
+ std::future<bool> disconnectResponseReceivedFuture =
+ disconnectResponseReceivedPromise.get_future();
+
+ auto futureTimeout = std::chrono::seconds(60);
+
+ auto connection = std::make_shared<DebuggerLocalConnection>();
+ std::unique_ptr<dap::Session> client = dap::Session::create();
+ client->registerHandler([&](const dap::InitializedEvent& e) {
+ (void)e;
+ initializedEventReceivedPromise.set_value(true);
+ });
+ client->registerHandler([&](const dap::ExitedEvent& e) {
+ (void)e;
+ exitedEventReceivedPromise.set_value(true);
+ });
+ client->registerHandler([&](const dap::TerminatedEvent& e) {
+ (void)e;
+ terminatedEventReceivedPromise.set_value(true);
+ });
+ client->registerHandler([&](const dap::ThreadEvent& e) {
+ if (e.reason == "started") {
+ threadStartedPromise.set_value(true);
+ } else if (e.reason == "exited") {
+ threadExitedPromise.set_value(true);
+ }
+ });
+
+ client->bind(connection->DebuggerToClient, connection->ClientToDebugger);
+
+ ScopedThread debuggerThread([&]() -> int {
+ std::shared_ptr<cmDebugger::cmDebuggerAdapter> debuggerAdapter =
+ std::make_shared<cmDebugger::cmDebuggerAdapter>(
+ connection, dap::file(stdout, false));
+
+ debuggerAdapterInitializedPromise.set_value(true);
+ debuggerAdapter->ReportExitCode(0);
+
+ // Ensure the disconnectResponse has been received before
+ // destructing debuggerAdapter.
+ ASSERT_TRUE(disconnectResponseReceivedFuture.wait_for(futureTimeout) ==
+ std::future_status::ready);
+ return 0;
+ });
+
+ dap::CMakeInitializeRequest initializeRequest;
+ auto initializeResponse = client->send(initializeRequest).get();
+ ASSERT_TRUE(initializeResponse.response.cmakeVersion.full == CMake_VERSION);
+ ASSERT_TRUE(initializeResponse.response.cmakeVersion.major ==
+ CMake_VERSION_MAJOR);
+ ASSERT_TRUE(initializeResponse.response.cmakeVersion.minor ==
+ CMake_VERSION_MINOR);
+ ASSERT_TRUE(initializeResponse.response.cmakeVersion.patch ==
+ CMake_VERSION_PATCH);
+ ASSERT_TRUE(initializeResponse.response.supportsExceptionInfoRequest);
+ ASSERT_TRUE(
+ initializeResponse.response.exceptionBreakpointFilters.has_value());
+
+ dap::LaunchRequest launchRequest;
+ auto launchResponse = client->send(launchRequest).get();
+ ASSERT_TRUE(!launchResponse.error);
+
+ dap::ConfigurationDoneRequest configurationDoneRequest;
+ auto configurationDoneResponse =
+ client->send(configurationDoneRequest).get();
+ ASSERT_TRUE(!configurationDoneResponse.error);
+
+ ASSERT_TRUE(debuggerAdapterInitializedFuture.wait_for(futureTimeout) ==
+ std::future_status::ready);
+ ASSERT_TRUE(initializedEventReceivedFuture.wait_for(futureTimeout) ==
+ std::future_status::ready);
+ ASSERT_TRUE(threadStartedFuture.wait_for(futureTimeout) ==
+ std::future_status::ready);
+ ASSERT_TRUE(threadExitedFuture.wait_for(futureTimeout) ==
+ std::future_status::ready);
+ ASSERT_TRUE(exitedEventReceivedFuture.wait_for(futureTimeout) ==
+ std::future_status::ready);
+ ASSERT_TRUE(terminatedEventReceivedFuture.wait_for(futureTimeout) ==
+ std::future_status::ready);
+
+ dap::DisconnectRequest disconnectRequest;
+ auto disconnectResponse = client->send(disconnectRequest).get();
+ disconnectResponseReceivedPromise.set_value(true);
+ ASSERT_TRUE(!disconnectResponse.error);
+
+ return true;
+}
+
+int testDebuggerAdapter(int, char*[])
+{
+ return runTests(std::vector<std::function<bool()>>{
+ testBasicProtocol,
+ });
+}