/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying file Copyright.txt or https://cmake.org/licensing for details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "cmDebuggerAdapter.h" #include "cmDebuggerProtocol.h" #include "cmVersionConfig.h" #ifdef _WIN32 # include "cmCryptoHash.h" # include "cmDebuggerWindowsPipeConnection.h" # include "cmSystemTools.h" #else # include "cmDebuggerPosixPipeConnection.h" #endif #include "testCommon.h" #include "testDebugger.h" bool testProtocolWithPipes() { std::promise debuggerConnectionCreatedPromise; std::future debuggerConnectionCreatedFuture = debuggerConnectionCreatedPromise.get_future(); std::future startedListeningFuture; std::promise debuggerAdapterInitializedPromise; std::future debuggerAdapterInitializedFuture = debuggerAdapterInitializedPromise.get_future(); std::promise initializedEventReceivedPromise; std::future initializedEventReceivedFuture = initializedEventReceivedPromise.get_future(); std::promise exitedEventReceivedPromise; std::future exitedEventReceivedFuture = exitedEventReceivedPromise.get_future(); std::promise terminatedEventReceivedPromise; std::future terminatedEventReceivedFuture = terminatedEventReceivedPromise.get_future(); std::promise threadStartedPromise; std::future threadStartedFuture = threadStartedPromise.get_future(); std::promise threadExitedPromise; std::future threadExitedFuture = threadExitedPromise.get_future(); std::promise disconnectResponseReceivedPromise; std::future disconnectResponseReceivedFuture = disconnectResponseReceivedPromise.get_future(); auto futureTimeout = std::chrono::seconds(60); #ifdef _WIN32 std::string namedPipe = R"(\\.\pipe\LOCAL\CMakeDebuggerPipe2_)" + cmCryptoHash(cmCryptoHash::AlgoSHA256) .HashString(cmSystemTools::GetCurrentWorkingDirectory()); #else std::string namedPipe = "CMakeDebuggerPipe2"; #endif std::unique_ptr 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); } }); ScopedThread debuggerThread([&]() -> int { try { auto connection = std::make_shared(namedPipe); startedListeningFuture = connection->StartedListening.get_future(); debuggerConnectionCreatedPromise.set_value(); std::shared_ptr debuggerAdapter = std::make_shared( 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; } catch (const std::runtime_error& error) { std::cerr << "Error: Failed to create debugger adapter.\n"; std::cerr << error.what() << "\n"; return -1; } }); ASSERT_TRUE(debuggerConnectionCreatedFuture.wait_for(futureTimeout) == std::future_status::ready); ASSERT_TRUE(startedListeningFuture.wait_for(futureTimeout) == std::future_status::ready); auto client2Debugger = std::make_shared(namedPipe); client2Debugger->WaitForConnection(); client->bind(client2Debugger, client2Debugger); dap::CMakeInitializeRequest initializeRequest; auto response = client->send(initializeRequest); auto initializeResponse = response.get(); ASSERT_TRUE(!initializeResponse.error); 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(terminatedEventReceivedFuture.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); dap::DisconnectRequest disconnectRequest; auto disconnectResponse = client->send(disconnectRequest).get(); disconnectResponseReceivedPromise.set_value(true); ASSERT_TRUE(!disconnectResponse.error); return true; } int testDebuggerAdapterPipe(int, char*[]) { return runTests({ testProtocolWithPipes }); }