Skip to content

[lldb-dap] Setup DAP for unit testing. #139937

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lldb/tools/lldb-dap/DAP.h
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,8 @@ struct DAP {
/// \param[in] default_repl_mode
/// Default repl mode behavior, as configured by the binary.
/// \param[in] pre_init_commands
/// LLDB commands to execute as soon as the debugger instance is allocaed.
/// LLDB commands to execute as soon as the debugger instance is
/// allocated.
/// \param[in] transport
/// Transport for this debug session.
DAP(Log *log, const ReplMode default_repl_mode,
Expand Down
5 changes: 4 additions & 1 deletion lldb/unittests/DAP/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
add_lldb_unittest(DAPTests
DAPTest.cpp
Handler/DisconnectTest.cpp
JSONUtilsTest.cpp
LLDBUtilsTest.cpp
TransportTest.cpp
ProtocolTypesTest.cpp
TestBase.cpp
TransportTest.cpp

LINK_LIBS
lldbDAP
Expand Down
38 changes: 38 additions & 0 deletions lldb/unittests/DAP/DAPTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//===-- DAPTest.cpp -------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "DAP.h"
#include "Protocol/ProtocolBase.h"
#include "TestBase.h"
#include "Transport.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
#include <chrono>
#include <memory>
#include <optional>

using namespace llvm;
using namespace lldb;
using namespace lldb_dap;
using namespace lldb_dap_tests;
using namespace lldb_dap::protocol;

class DAPTest : public TransportBase {};

TEST_F(DAPTest, SendProtocolMessages) {
DAP dap{
/*log=*/nullptr,
/*default_repl_mode=*/ReplMode::Auto,
/*pre_init_commands=*/{},
/*transport=*/*to_dap,
};
dap.Send(Event{/*event=*/"my-event", /*body=*/std::nullopt});
ASSERT_THAT_EXPECTED(from_dap->Read(std::chrono::milliseconds(1)),
HasValue(testing::VariantWith<Event>(testing::FieldsAre(
/*event=*/"my-event", /*body=*/std::nullopt))));
}
35 changes: 35 additions & 0 deletions lldb/unittests/DAP/Handler/DisconnectTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//===-- DisconnectTest.cpp ------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "DAP.h"
#include "Handler/RequestHandler.h"
#include "Protocol/ProtocolBase.h"
#include "TestBase.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
#include <memory>
#include <optional>

using namespace llvm;
using namespace lldb;
using namespace lldb_dap;
using namespace lldb_dap_tests;
using namespace lldb_dap::protocol;

class DisconnectRequestHandlerTest : public DAPTestBase {};

TEST_F(DisconnectRequestHandlerTest, DisconnectingTriggersTerminated) {
DisconnectRequestHandler handler(*dap);
EXPECT_FALSE(dap->disconnecting);
ASSERT_THAT_ERROR(handler.Run(std::nullopt), Succeeded());
EXPECT_TRUE(dap->disconnecting);
std::vector<Message> messages = DrainOutput();
EXPECT_THAT(messages,
testing::Contains(testing::VariantWith<Event>(testing::FieldsAre(
/*event=*/"terminated", /*body=*/std::nullopt))));
}
70 changes: 70 additions & 0 deletions lldb/unittests/DAP/TestBase.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//===-- TestBase.cpp ------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "TestBase.h"
#include "Protocol/ProtocolBase.h"
#include "lldb/Host/File.h"
#include "lldb/Host/Pipe.h"
#include "llvm/Testing/Support/Error.h"

using namespace llvm;
using namespace lldb;
using namespace lldb_dap;
using namespace lldb_dap::protocol;
using namespace lldb_dap_tests;
using lldb_private::File;
using lldb_private::NativeFile;
using lldb_private::Pipe;

void PipeBase::SetUp() {
ASSERT_THAT_ERROR(input.CreateNew(false).ToError(), Succeeded());
ASSERT_THAT_ERROR(output.CreateNew(false).ToError(), Succeeded());
}

void TransportBase::SetUp() {
PipeBase::SetUp();
to_dap = std::make_unique<Transport>(
"to_dap", nullptr,
std::make_shared<NativeFile>(input.GetReadFileDescriptor(),
File::eOpenOptionReadOnly,
NativeFile::Unowned),
std::make_shared<NativeFile>(output.GetWriteFileDescriptor(),
File::eOpenOptionWriteOnly,
NativeFile::Unowned));
from_dap = std::make_unique<Transport>(
"from_dap", nullptr,
std::make_shared<NativeFile>(output.GetReadFileDescriptor(),
File::eOpenOptionReadOnly,
NativeFile::Unowned),
std::make_shared<NativeFile>(input.GetWriteFileDescriptor(),
File::eOpenOptionWriteOnly,
NativeFile::Unowned));
}

void DAPTestBase::SetUp() {
TransportBase::SetUp();
dap = std::make_unique<DAP>(
/*log=*/nullptr,
/*default_repl_mode=*/ReplMode::Auto,
/*pre_init_commands=*/std::vector<std::string>(),
/*transport=*/*to_dap);
}

std::vector<Message> DAPTestBase::DrainOutput() {
std::vector<Message> msgs;
output.CloseWriteFileDescriptor();
while (true) {
Expected<Message> next = from_dap->Read(std::chrono::milliseconds(1));
if (!next) {
consumeError(next.takeError());
break;
}
msgs.push_back(*next);
}
return msgs;
}
48 changes: 48 additions & 0 deletions lldb/unittests/DAP/TestBase.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//===-- TestBase.cpp ------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "DAP.h"
#include "Protocol/ProtocolBase.h"
#include "Transport.h"
#include "lldb/Host/Pipe.h"
#include "gtest/gtest.h"

namespace lldb_dap_tests {

/// A base class for tests that need a pair of pipes for communication.
class PipeBase : public testing::Test {
protected:
lldb_private::Pipe input;
lldb_private::Pipe output;

void SetUp() override;
};

/// A base class for tests that need transport configured for communicating DAP
/// messages.
class TransportBase : public PipeBase {
protected:
std::unique_ptr<lldb_dap::Transport> to_dap;
std::unique_ptr<lldb_dap::Transport> from_dap;

void SetUp() override;
};

/// A base class for tests that interact with a `lldb_dap::DAP` instance.
class DAPTestBase : public TransportBase {
protected:
std::unique_ptr<lldb_dap::DAP> dap;

void SetUp() override;

/// Closes the DAP output pipe and returns the remaining protocol messages in
/// the buffer.
std::vector<lldb_dap::protocol::Message> DrainOutput();
};

} // namespace lldb_dap_tests
24 changes: 10 additions & 14 deletions lldb/unittests/DAP/TransportTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@

#include "Transport.h"
#include "Protocol/ProtocolBase.h"
#include "TestBase.h"
#include "lldb/Host/File.h"
#include "lldb/Host/Pipe.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
#include <chrono>
Expand All @@ -21,20 +21,18 @@
using namespace llvm;
using namespace lldb;
using namespace lldb_dap;
using namespace lldb_dap_tests;
using namespace lldb_dap::protocol;
using lldb_private::File;
using lldb_private::NativeFile;
using lldb_private::Pipe;

class TransportTest : public testing::Test {
class TransportTest : public PipeBase {
protected:
Pipe input;
Pipe output;
std::unique_ptr<Transport> transport;

void SetUp() override {
ASSERT_THAT_ERROR(input.CreateNew(false).ToError(), Succeeded());
ASSERT_THAT_ERROR(output.CreateNew(false).ToError(), Succeeded());
PipeBase::SetUp();
transport = std::make_unique<Transport>(
"stdio", nullptr,
std::make_shared<NativeFile>(input.GetReadFileDescriptor(),
Expand All @@ -44,13 +42,6 @@ class TransportTest : public testing::Test {
File::eOpenOptionWriteOnly,
NativeFile::Unowned));
}

void Write(StringRef json) {
std::string message =
formatv("Content-Length: {0}\r\n\r\n{1}", json.size(), json).str();
ASSERT_THAT_EXPECTED(input.Write(message.data(), message.size()),
Succeeded());
}
};

TEST_F(TransportTest, MalformedRequests) {
Expand All @@ -65,7 +56,12 @@ TEST_F(TransportTest, MalformedRequests) {
}

TEST_F(TransportTest, Read) {
Write(R"json({"seq": 1, "type": "request", "command": "abc"})json");
std::string json =
R"json({"seq": 1, "type": "request", "command": "abc"})json";
std::string message =
formatv("Content-Length: {0}\r\n\r\n{1}", json.size(), json).str();
ASSERT_THAT_EXPECTED(input.Write(message.data(), message.size()),
Succeeded());
ASSERT_THAT_EXPECTED(
transport->Read(std::chrono::milliseconds(1)),
HasValue(testing::VariantWith<Request>(testing::FieldsAre(
Expand Down
Loading