Skip to content

Commit 58b9b86

Browse files
ashgtiJDevlieghere
andauthored
[lldb-dap] Setup DAP for unit testing. (#139937)
This is a very simple case that currently only validates we can create a DAP instance and send a message over the transport layer. More in-depth tests will require additional helpers and possibly refactors of DAP to make it more testable, however this is some ground work to have basic support for unit tests. --------- Co-authored-by: Jonas Devlieghere <jonas@devlieghere.com>
1 parent 682a976 commit 58b9b86

File tree

7 files changed

+207
-16
lines changed

7 files changed

+207
-16
lines changed

lldb/tools/lldb-dap/DAP.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,8 @@ struct DAP {
226226
/// \param[in] default_repl_mode
227227
/// Default repl mode behavior, as configured by the binary.
228228
/// \param[in] pre_init_commands
229-
/// LLDB commands to execute as soon as the debugger instance is allocaed.
229+
/// LLDB commands to execute as soon as the debugger instance is
230+
/// allocated.
230231
/// \param[in] transport
231232
/// Transport for this debug session.
232233
DAP(Log *log, const ReplMode default_repl_mode,

lldb/unittests/DAP/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
add_lldb_unittest(DAPTests
2+
DAPTest.cpp
3+
Handler/DisconnectTest.cpp
24
JSONUtilsTest.cpp
35
LLDBUtilsTest.cpp
4-
TransportTest.cpp
56
ProtocolTypesTest.cpp
7+
TestBase.cpp
8+
TransportTest.cpp
69

710
LINK_LIBS
811
lldbDAP

lldb/unittests/DAP/DAPTest.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//===-- DAPTest.cpp -------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "DAP.h"
10+
#include "Protocol/ProtocolBase.h"
11+
#include "TestBase.h"
12+
#include "Transport.h"
13+
#include "llvm/Testing/Support/Error.h"
14+
#include "gtest/gtest.h"
15+
#include <chrono>
16+
#include <memory>
17+
#include <optional>
18+
19+
using namespace llvm;
20+
using namespace lldb;
21+
using namespace lldb_dap;
22+
using namespace lldb_dap_tests;
23+
using namespace lldb_dap::protocol;
24+
25+
class DAPTest : public TransportBase {};
26+
27+
TEST_F(DAPTest, SendProtocolMessages) {
28+
DAP dap{
29+
/*log=*/nullptr,
30+
/*default_repl_mode=*/ReplMode::Auto,
31+
/*pre_init_commands=*/{},
32+
/*transport=*/*to_dap,
33+
};
34+
dap.Send(Event{/*event=*/"my-event", /*body=*/std::nullopt});
35+
ASSERT_THAT_EXPECTED(from_dap->Read(std::chrono::milliseconds(1)),
36+
HasValue(testing::VariantWith<Event>(testing::FieldsAre(
37+
/*event=*/"my-event", /*body=*/std::nullopt))));
38+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//===-- DisconnectTest.cpp ------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "DAP.h"
10+
#include "Handler/RequestHandler.h"
11+
#include "Protocol/ProtocolBase.h"
12+
#include "TestBase.h"
13+
#include "llvm/Testing/Support/Error.h"
14+
#include "gtest/gtest.h"
15+
#include <memory>
16+
#include <optional>
17+
18+
using namespace llvm;
19+
using namespace lldb;
20+
using namespace lldb_dap;
21+
using namespace lldb_dap_tests;
22+
using namespace lldb_dap::protocol;
23+
24+
class DisconnectRequestHandlerTest : public DAPTestBase {};
25+
26+
TEST_F(DisconnectRequestHandlerTest, DisconnectingTriggersTerminated) {
27+
DisconnectRequestHandler handler(*dap);
28+
EXPECT_FALSE(dap->disconnecting);
29+
ASSERT_THAT_ERROR(handler.Run(std::nullopt), Succeeded());
30+
EXPECT_TRUE(dap->disconnecting);
31+
std::vector<Message> messages = DrainOutput();
32+
EXPECT_THAT(messages,
33+
testing::Contains(testing::VariantWith<Event>(testing::FieldsAre(
34+
/*event=*/"terminated", /*body=*/std::nullopt))));
35+
}

lldb/unittests/DAP/TestBase.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//===-- TestBase.cpp ------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "TestBase.h"
10+
#include "Protocol/ProtocolBase.h"
11+
#include "lldb/Host/File.h"
12+
#include "lldb/Host/Pipe.h"
13+
#include "llvm/Testing/Support/Error.h"
14+
15+
using namespace llvm;
16+
using namespace lldb;
17+
using namespace lldb_dap;
18+
using namespace lldb_dap::protocol;
19+
using namespace lldb_dap_tests;
20+
using lldb_private::File;
21+
using lldb_private::NativeFile;
22+
using lldb_private::Pipe;
23+
24+
void PipeBase::SetUp() {
25+
ASSERT_THAT_ERROR(input.CreateNew(false).ToError(), Succeeded());
26+
ASSERT_THAT_ERROR(output.CreateNew(false).ToError(), Succeeded());
27+
}
28+
29+
void TransportBase::SetUp() {
30+
PipeBase::SetUp();
31+
to_dap = std::make_unique<Transport>(
32+
"to_dap", nullptr,
33+
std::make_shared<NativeFile>(input.GetReadFileDescriptor(),
34+
File::eOpenOptionReadOnly,
35+
NativeFile::Unowned),
36+
std::make_shared<NativeFile>(output.GetWriteFileDescriptor(),
37+
File::eOpenOptionWriteOnly,
38+
NativeFile::Unowned));
39+
from_dap = std::make_unique<Transport>(
40+
"from_dap", nullptr,
41+
std::make_shared<NativeFile>(output.GetReadFileDescriptor(),
42+
File::eOpenOptionReadOnly,
43+
NativeFile::Unowned),
44+
std::make_shared<NativeFile>(input.GetWriteFileDescriptor(),
45+
File::eOpenOptionWriteOnly,
46+
NativeFile::Unowned));
47+
}
48+
49+
void DAPTestBase::SetUp() {
50+
TransportBase::SetUp();
51+
dap = std::make_unique<DAP>(
52+
/*log=*/nullptr,
53+
/*default_repl_mode=*/ReplMode::Auto,
54+
/*pre_init_commands=*/std::vector<std::string>(),
55+
/*transport=*/*to_dap);
56+
}
57+
58+
std::vector<Message> DAPTestBase::DrainOutput() {
59+
std::vector<Message> msgs;
60+
output.CloseWriteFileDescriptor();
61+
while (true) {
62+
Expected<Message> next = from_dap->Read(std::chrono::milliseconds(1));
63+
if (!next) {
64+
consumeError(next.takeError());
65+
break;
66+
}
67+
msgs.push_back(*next);
68+
}
69+
return msgs;
70+
}

lldb/unittests/DAP/TestBase.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
//===-- TestBase.cpp ------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "DAP.h"
10+
#include "Protocol/ProtocolBase.h"
11+
#include "Transport.h"
12+
#include "lldb/Host/Pipe.h"
13+
#include "gtest/gtest.h"
14+
15+
namespace lldb_dap_tests {
16+
17+
/// A base class for tests that need a pair of pipes for communication.
18+
class PipeBase : public testing::Test {
19+
protected:
20+
lldb_private::Pipe input;
21+
lldb_private::Pipe output;
22+
23+
void SetUp() override;
24+
};
25+
26+
/// A base class for tests that need transport configured for communicating DAP
27+
/// messages.
28+
class TransportBase : public PipeBase {
29+
protected:
30+
std::unique_ptr<lldb_dap::Transport> to_dap;
31+
std::unique_ptr<lldb_dap::Transport> from_dap;
32+
33+
void SetUp() override;
34+
};
35+
36+
/// A base class for tests that interact with a `lldb_dap::DAP` instance.
37+
class DAPTestBase : public TransportBase {
38+
protected:
39+
std::unique_ptr<lldb_dap::DAP> dap;
40+
41+
void SetUp() override;
42+
43+
/// Closes the DAP output pipe and returns the remaining protocol messages in
44+
/// the buffer.
45+
std::vector<lldb_dap::protocol::Message> DrainOutput();
46+
};
47+
48+
} // namespace lldb_dap_tests

lldb/unittests/DAP/TransportTest.cpp

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88

99
#include "Transport.h"
1010
#include "Protocol/ProtocolBase.h"
11+
#include "TestBase.h"
1112
#include "lldb/Host/File.h"
1213
#include "lldb/Host/Pipe.h"
1314
#include "llvm/ADT/StringRef.h"
14-
#include "llvm/Support/FormatVariadic.h"
1515
#include "llvm/Testing/Support/Error.h"
1616
#include "gtest/gtest.h"
1717
#include <chrono>
@@ -21,20 +21,18 @@
2121
using namespace llvm;
2222
using namespace lldb;
2323
using namespace lldb_dap;
24+
using namespace lldb_dap_tests;
2425
using namespace lldb_dap::protocol;
2526
using lldb_private::File;
2627
using lldb_private::NativeFile;
2728
using lldb_private::Pipe;
2829

29-
class TransportTest : public testing::Test {
30+
class TransportTest : public PipeBase {
3031
protected:
31-
Pipe input;
32-
Pipe output;
3332
std::unique_ptr<Transport> transport;
3433

3534
void SetUp() override {
36-
ASSERT_THAT_ERROR(input.CreateNew(false).ToError(), Succeeded());
37-
ASSERT_THAT_ERROR(output.CreateNew(false).ToError(), Succeeded());
35+
PipeBase::SetUp();
3836
transport = std::make_unique<Transport>(
3937
"stdio", nullptr,
4038
std::make_shared<NativeFile>(input.GetReadFileDescriptor(),
@@ -44,13 +42,6 @@ class TransportTest : public testing::Test {
4442
File::eOpenOptionWriteOnly,
4543
NativeFile::Unowned));
4644
}
47-
48-
void Write(StringRef json) {
49-
std::string message =
50-
formatv("Content-Length: {0}\r\n\r\n{1}", json.size(), json).str();
51-
ASSERT_THAT_EXPECTED(input.Write(message.data(), message.size()),
52-
Succeeded());
53-
}
5445
};
5546

5647
TEST_F(TransportTest, MalformedRequests) {
@@ -65,7 +56,12 @@ TEST_F(TransportTest, MalformedRequests) {
6556
}
6657

6758
TEST_F(TransportTest, Read) {
68-
Write(R"json({"seq": 1, "type": "request", "command": "abc"})json");
59+
std::string json =
60+
R"json({"seq": 1, "type": "request", "command": "abc"})json";
61+
std::string message =
62+
formatv("Content-Length: {0}\r\n\r\n{1}", json.size(), json).str();
63+
ASSERT_THAT_EXPECTED(input.Write(message.data(), message.size()),
64+
Succeeded());
6965
ASSERT_THAT_EXPECTED(
7066
transport->Read(std::chrono::milliseconds(1)),
7167
HasValue(testing::VariantWith<Request>(testing::FieldsAre(

0 commit comments

Comments
 (0)