diff options
Diffstat (limited to 'Tests/CMakeLib/testDebuggerAdapter.cxx')
-rw-r--r-- | Tests/CMakeLib/testDebuggerAdapter.cxx | 173 |
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, + }); +} |