1 //===-- JSONTransportTests.cpp -------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
10 #include "support/Cancellation.h"
11 #include "gmock/gmock.h"
12 #include "gtest/gtest.h"
19 // No fmemopen on windows or on versions of MacOS X earlier than 10.13, so we
20 // can't easily run this test.
21 #if !(defined(_WIN32) || (defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
22 __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101300))
24 // Fixture takes care of managing the input/output buffers for the transport.
25 class JSONTransportTest
: public ::testing::Test
{
26 std::string InBuf
, OutBuf
, MirrorBuf
;
27 llvm::raw_string_ostream Out
, Mirror
;
28 std::unique_ptr
<FILE, int (*)(FILE *)> In
;
31 JSONTransportTest() : Out(OutBuf
), Mirror(MirrorBuf
), In(nullptr, nullptr) {}
33 template <typename
... Args
>
34 std::unique_ptr
<Transport
> transport(std::string InData
, bool Pretty
,
35 JSONStreamStyle Style
) {
36 InBuf
= std::move(InData
);
37 In
= {fmemopen(&InBuf
[0], InBuf
.size(), "r"), &fclose
};
38 return newJSONTransport(In
.get(), Out
, &Mirror
, Pretty
, Style
);
41 std::string
input() const { return InBuf
; }
42 std::string
output() { return Out
.str(); }
43 std::string
inputMirror() { return Mirror
.str(); }
46 // Echo is a simple server running on a transport:
47 // - logs each message it gets.
48 // - when it gets a call, replies to it
49 // - when it gets a notification for method "call", makes a call on Target
50 // Hangs up when it gets an exit notification.
51 class Echo
: public Transport::MessageHandler
{
54 llvm::raw_string_ostream Log
;
57 Echo(Transport
&Target
) : Target(Target
), Log(LogBuf
) {}
59 std::string
log() { return Log
.str(); }
61 bool onNotify(llvm::StringRef Method
, llvm::json::Value Params
) override
{
62 Log
<< "Notification " << Method
<< ": " << Params
<< "\n";
64 Target
.call("echo call", std::move(Params
), 42);
65 return Method
!= "exit";
68 bool onCall(llvm::StringRef Method
, llvm::json::Value Params
,
69 llvm::json::Value ID
) override
{
70 Log
<< "Call " << Method
<< "(" << ID
<< "): " << Params
<< "\n";
73 ID
, llvm::make_error
<LSPError
>("trouble at mill", ErrorCode(88)));
74 else if (Method
== "invalidated") // gone out skew on treadle
75 Target
.reply(ID
, llvm::make_error
<CancelledError
>(
76 static_cast<int>(ErrorCode::ContentModified
)));
78 Target
.reply(ID
, std::move(Params
));
82 bool onReply(llvm::json::Value ID
,
83 llvm::Expected
<llvm::json::Value
> Params
) override
{
85 Log
<< "Reply(" << ID
<< "): " << *Params
<< "\n";
88 << "): error = " << llvm::toString(Params
.takeError()) << "\n";
93 std::string
trim(llvm::StringRef S
) { return S
.trim().str(); }
95 // Runs an Echo session using the standard JSON-RPC format we use in production.
96 TEST_F(JSONTransportTest
, StandardDense
) {
98 "Content-Length: 52\r\n\r\n"
99 R
"({"jsonrpc
": "2.0", "method
": "call
", "params
": 1234})"
100 "Content-Length: 46\r\n\r\n"
101 R
"({"jsonrpc
": "2.0", "id
": 1234, "result
": 5678})"
102 "Content-Length: 67\r\n\r\n"
103 R
"({"jsonrpc
": "2.0", "method
": "foo
", "id
": "abcd
", "params
": "efgh
"})"
104 "Content-Length: 73\r\n\r\n"
105 R
"({"jsonrpc
": "2.0", "id
": "xyz
", "error
": {"code
": 99, "message
": "bad
!"}})"
106 "Content-Length: 68\r\n\r\n"
107 R
"({"jsonrpc
": "2.0", "method
": "err
", "id
": "wxyz
", "params
": "boom
!"})"
108 "Content-Length: 36\r\n\r\n"
109 R
"({"jsonrpc
": "2.0", "method
": "exit
"})",
110 /*Pretty=*/false, JSONStreamStyle::Standard
);
112 auto Err
= T
->loop(E
);
113 EXPECT_FALSE(bool(Err
)) << toString(std::move(Err
));
115 const char *WantLog
= R
"(
116 Notification call: 1234
118 Call foo("abcd
"): "efgh
"
119 Reply("xyz
"): error = 99: bad!
120 Call err("wxyz
"): "boom
!"
121 Notification exit: null
123 EXPECT_EQ(trim(E
.log()), trim(WantLog
));
124 const char *WantOutput
=
125 "Content-Length: 60\r\n\r\n"
126 R
"({"id
":42,"jsonrpc
":"2.0","method
":"echo call
","params
":1234})"
127 "Content-Length: 45\r\n\r\n"
128 R
"({"id
":"abcd
","jsonrpc
":"2.0","result
":"efgh
"})"
129 "Content-Length: 77\r\n\r\n"
130 R
"({"error
":{"code
":88,"message
":"trouble at mill
"},"id
":"wxyz
","jsonrpc
":"2.0"})";
131 EXPECT_EQ(output(), WantOutput
);
132 EXPECT_EQ(trim(inputMirror()), trim(input()));
135 // Runs an Echo session using the "delimited" input and pretty-printed output
136 // that we use in lit tests.
137 TEST_F(JSONTransportTest
, DelimitedPretty
) {
138 auto T
= transport(R
"jsonrpc(
139 {"jsonrpc
": "2.0", "method
": "call
", "params
": 1234}
141 {"jsonrpc
": "2.0", "id
": 1234, "result
": 5678}
143 {"jsonrpc
": "2.0", "method
": "foo
", "id
": "abcd
", "params
": "efgh
"}
145 {"jsonrpc
": "2.0", "id
": "xyz
", "error
": {"code
": 99, "message
": "bad
!"}}
147 {"jsonrpc
": "2.0", "method
": "invalidated
", "id
": "wxyz
", "params
": "boom
!"}
149 {"jsonrpc
": "2.0", "method
": "exit
"}
151 /*Pretty=*/true, JSONStreamStyle::Delimited
);
153 auto Err
= T
->loop(E
);
154 EXPECT_FALSE(bool(Err
)) << toString(std::move(Err
));
156 const char *WantLog
= R
"(
157 Notification call: 1234
159 Call foo("abcd
"): "efgh
"
160 Reply("xyz
"): error = 99: bad!
161 Call invalidated("wxyz
"): "boom
!"
162 Notification exit: null
164 EXPECT_EQ(trim(E
.log()), trim(WantLog
));
165 const char *WantOutput
= "Content-Length: 77\r\n\r\n"
169 "method
": "echo call
",
172 "Content-Length: 58\r\n\r\n"
178 "Content-Length: 145\r\n\r\n"
182 "message
": "Request cancelled because the document was modified
"
187 EXPECT_EQ(output(), WantOutput
);
188 EXPECT_EQ(trim(inputMirror()), trim(input()));
191 // IO errors such as EOF ane reported.
192 // The only successful return from loop() is if a handler returned false.
193 TEST_F(JSONTransportTest
, EndOfFile
) {
194 auto T
= transport("Content-Length: 52\r\n\r\n"
195 R
"({"jsonrpc
": "2.0", "method
": "call
", "params
": 1234})",
196 /*Pretty=*/false, JSONStreamStyle::Standard
);
198 auto Err
= T
->loop(E
);
199 EXPECT_EQ(trim(E
.log()), "Notification call: 1234");
200 EXPECT_TRUE(bool(Err
)); // Ran into EOF with no handler signalling done.
201 consumeError(std::move(Err
));
202 EXPECT_EQ(trim(inputMirror()), trim(input()));
208 } // namespace clangd