1//===-- JSONTransportTest.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 "lldb/Host/JSONTransport.h"
10#include "TestingSupport/Host/PipeTestUtilities.h"
11#include "lldb/Host/File.h"
12
13using namespace llvm;
14using namespace lldb_private;
15
16namespace {
17template <typename T> class JSONTransportTest : public PipePairTest {
18protected:
19 std::unique_ptr<JSONTransport> transport;
20
21 void SetUp() override {
22 PipePairTest::SetUp();
23 transport = std::make_unique<T>(
24 std::make_shared<NativeFile>(input.GetReadFileDescriptor(),
25 File::eOpenOptionReadOnly,
26 NativeFile::Unowned),
27 std::make_shared<NativeFile>(output.GetWriteFileDescriptor(),
28 File::eOpenOptionWriteOnly,
29 NativeFile::Unowned));
30 }
31};
32
33class HTTPDelimitedJSONTransportTest
34 : public JSONTransportTest<HTTPDelimitedJSONTransport> {
35public:
36 using JSONTransportTest::JSONTransportTest;
37};
38
39class JSONRPCTransportTest : public JSONTransportTest<JSONRPCTransport> {
40public:
41 using JSONTransportTest::JSONTransportTest;
42};
43
44struct JSONTestType {
45 std::string str;
46};
47
48llvm::json::Value toJSON(const JSONTestType &T) {
49 return llvm::json::Object{{.K: "str", .V: T.str}};
50}
51
52bool fromJSON(const llvm::json::Value &V, JSONTestType &T, llvm::json::Path P) {
53 llvm::json::ObjectMapper O(V, P);
54 return O && O.map(Prop: "str", Out&: T.str);
55}
56} // namespace
57
58TEST_F(HTTPDelimitedJSONTransportTest, MalformedRequests) {
59 std::string malformed_header = "COnTent-LenGth: -1{}\r\n\r\nnotjosn";
60 ASSERT_THAT_EXPECTED(
61 input.Write(malformed_header.data(), malformed_header.size()),
62 Succeeded());
63 ASSERT_THAT_EXPECTED(
64 transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
65 FailedWithMessage(
66 "expected 'Content-Length: ' and got 'COnTent-LenGth: '"));
67}
68
69TEST_F(HTTPDelimitedJSONTransportTest, Read) {
70 std::string json = R"json({"str": "foo"})json";
71 std::string message =
72 formatv(Fmt: "Content-Length: {0}\r\n\r\n{1}", Vals: json.size(), Vals&: json).str();
73 ASSERT_THAT_EXPECTED(input.Write(message.data(), message.size()),
74 Succeeded());
75 ASSERT_THAT_EXPECTED(
76 transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
77 HasValue(testing::FieldsAre(/*str=*/"foo")));
78}
79
80TEST_F(HTTPDelimitedJSONTransportTest, ReadWithEOF) {
81 input.CloseWriteFileDescriptor();
82 ASSERT_THAT_EXPECTED(
83 transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
84 Failed<TransportEOFError>());
85}
86
87
88TEST_F(HTTPDelimitedJSONTransportTest, InvalidTransport) {
89 transport = std::make_unique<HTTPDelimitedJSONTransport>(args: nullptr, args: nullptr);
90 ASSERT_THAT_EXPECTED(
91 transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
92 Failed<TransportInvalidError>());
93}
94
95TEST_F(HTTPDelimitedJSONTransportTest, Write) {
96 ASSERT_THAT_ERROR(transport->Write(JSONTestType{"foo"}), Succeeded());
97 output.CloseWriteFileDescriptor();
98 char buf[1024];
99 Expected<size_t> bytes_read =
100 output.Read(buf, size: sizeof(buf), timeout: std::chrono::milliseconds(1));
101 ASSERT_THAT_EXPECTED(bytes_read, Succeeded());
102 ASSERT_EQ(StringRef(buf, *bytes_read), StringRef("Content-Length: 13\r\n\r\n"
103 R"json({"str":"foo"})json"));
104}
105
106TEST_F(JSONRPCTransportTest, MalformedRequests) {
107 std::string malformed_header = "notjson\n";
108 ASSERT_THAT_EXPECTED(
109 input.Write(malformed_header.data(), malformed_header.size()),
110 Succeeded());
111 ASSERT_THAT_EXPECTED(
112 transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
113 llvm::Failed());
114}
115
116TEST_F(JSONRPCTransportTest, Read) {
117 std::string json = R"json({"str": "foo"})json";
118 std::string message = formatv(Fmt: "{0}\n", Vals&: json).str();
119 ASSERT_THAT_EXPECTED(input.Write(message.data(), message.size()),
120 Succeeded());
121 ASSERT_THAT_EXPECTED(
122 transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
123 HasValue(testing::FieldsAre(/*str=*/"foo")));
124}
125
126TEST_F(JSONRPCTransportTest, ReadWithEOF) {
127 input.CloseWriteFileDescriptor();
128 ASSERT_THAT_EXPECTED(
129 transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
130 Failed<TransportEOFError>());
131}
132
133TEST_F(JSONRPCTransportTest, Write) {
134 ASSERT_THAT_ERROR(transport->Write(JSONTestType{"foo"}), Succeeded());
135 output.CloseWriteFileDescriptor();
136 char buf[1024];
137 Expected<size_t> bytes_read =
138 output.Read(buf, size: sizeof(buf), timeout: std::chrono::milliseconds(1));
139 ASSERT_THAT_EXPECTED(bytes_read, Succeeded());
140 ASSERT_EQ(StringRef(buf, *bytes_read), StringRef(R"json({"str":"foo"})json"
141 "\n"));
142}
143
144TEST_F(JSONRPCTransportTest, InvalidTransport) {
145 transport = std::make_unique<JSONRPCTransport>(args: nullptr, args: nullptr);
146 ASSERT_THAT_EXPECTED(
147 transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
148 Failed<TransportInvalidError>());
149}
150
151#ifndef _WIN32
152TEST_F(HTTPDelimitedJSONTransportTest, ReadWithTimeout) {
153 ASSERT_THAT_EXPECTED(
154 transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
155 Failed<TransportTimeoutError>());
156}
157
158TEST_F(JSONRPCTransportTest, ReadWithTimeout) {
159 ASSERT_THAT_EXPECTED(
160 transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
161 Failed<TransportTimeoutError>());
162}
163
164// Windows CRT _read checks that the file descriptor is valid and calls a
165// handler if not. This handler is normally a breakpoint, which looks like a
166// crash when not handled by a debugger.
167// https://learn.microsoft.com/en-us/%20cpp/c-runtime-library/reference/read?view=msvc-170
168TEST_F(HTTPDelimitedJSONTransportTest, ReadAfterClosed) {
169 input.CloseReadFileDescriptor();
170 ASSERT_THAT_EXPECTED(
171 transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
172 llvm::Failed());
173}
174
175TEST_F(JSONRPCTransportTest, ReadAfterClosed) {
176 input.CloseReadFileDescriptor();
177 ASSERT_THAT_EXPECTED(
178 transport->Read<JSONTestType>(std::chrono::milliseconds(1)),
179 llvm::Failed());
180}
181#endif
182

source code of lldb/unittests/Host/JSONTransportTest.cpp