clang-tools 10.0.0

JSONTransportTests.cpp
Go to the documentation of this file.
1 //===-- JSONTransportTests.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 #include "Protocol.h"
9 #include "Transport.h"
10 #include "gmock/gmock.h"
11 #include "gtest/gtest.h"
12 #include <cstdio>
13
14 namespace clang {
15 namespace clangd {
16 namespace {
17
18 // No fmemopen on windows or on versions of MacOS X earlier than 10.13, so we
19 // can't easily run this test.
20 #if !(defined(_WIN32) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && \
21 __MAC_OS_X_VERSION_MIN_REQUIRED < 101300))
22
23 // Fixture takes care of managing the input/output buffers for the transport.
24 class JSONTransportTest : public ::testing::Test {
25 std::string InBuf, OutBuf, MirrorBuf;
26 llvm::raw_string_ostream Out, Mirror;
27 std::unique_ptr<FILE, int (*)(FILE *)> In;
28
29 protected:
30 JSONTransportTest() : Out(OutBuf), Mirror(MirrorBuf), In(nullptr, nullptr) {}
31
32 template <typename... Args>
33 std::unique_ptr<Transport> transport(std::string InData, bool Pretty,
34 JSONStreamStyle Style) {
35 InBuf = std::move(InData);
36 In = {fmemopen(&InBuf[0], InBuf.size(), "r"), &fclose};
37 return newJSONTransport(In.get(), Out, &Mirror, Pretty, Style);
38 }
39
40 std::string input() const { return InBuf; }
41 std::string output() { return Out.str(); }
42 std::string input_mirror() { return Mirror.str(); }
43 };
44
45 // Echo is a simple server running on a transport:
46 // - logs each message it gets.
47 // - when it gets a call, replies to it
48 // - when it gets a notification for method "call", makes a call on Target
49 // Hangs up when it gets an exit notification.
50 class Echo : public Transport::MessageHandler {
51 Transport &Target;
52 std::string LogBuf;
53 llvm::raw_string_ostream Log;
54
55 public:
56 Echo(Transport &Target) : Target(Target), Log(LogBuf) {}
57
58 std::string log() { return Log.str(); }
59
60 bool onNotify(llvm::StringRef Method, llvm::json::Value Params) override {
61 Log << "Notification " << Method << ": " << Params << "\n";
62 if (Method == "call")
63 Target.call("echo call", std::move(Params), 42);
64 return Method != "exit";
65 }
66
67 bool onCall(llvm::StringRef Method, llvm::json::Value Params,
68 llvm::json::Value ID) override {
69 Log << "Call " << Method << "(" << ID << "): " << Params << "\n";
70 if (Method == "err")
71 Target.reply(
72 ID, llvm::make_error<LSPError>("trouble at mill", ErrorCode(88)));
73 else
74 Target.reply(ID, std::move(Params));
75 return true;
76 }
77
78 bool onReply(llvm::json::Value ID,
79 llvm::Expected<llvm::json::Value> Params) override {
80 if (Params)
81 Log << "Reply(" << ID << "): " << *Params << "\n";
82 else
83 Log << "Reply(" << ID
84 << "): error = " << llvm::toString(Params.takeError()) << "\n";
85 return true;
86 }
87 };
88
89 std::string trim(llvm::StringRef S) { return S.trim().str(); }
90
91 // Runs an Echo session using the standard JSON-RPC format we use in production.
92 TEST_F(JSONTransportTest, StandardDense) {
93 auto T = transport(
94 "Content-Length: 52\r\n\r\n"
95 R"({"jsonrpc": "2.0", "method": "call", "params": 1234})"
96 "Content-Length: 46\r\n\r\n"
97 R"({"jsonrpc": "2.0", "id": 1234, "result": 5678})"
98 "Content-Length: 67\r\n\r\n"
99 R"({"jsonrpc": "2.0", "method": "foo", "id": "abcd", "params": "efgh"})"
100 "Content-Length: 73\r\n\r\n"
101 R"({"jsonrpc": "2.0", "id": "xyz", "error": {"code": 99, "message": "bad!"}})"
102 "Content-Length: 68\r\n\r\n"
103 R"({"jsonrpc": "2.0", "method": "err", "id": "wxyz", "params": "boom!"})"
104 "Content-Length: 36\r\n\r\n"
105 R"({"jsonrpc": "2.0", "method": "exit"})",
106 /*Pretty=*/false, JSONStreamStyle::Standard);
107 Echo E(*T);
108 auto Err = T->loop(E);
109 EXPECT_FALSE(bool(Err)) << toString(std::move(Err));
110
111 const char *WantLog = R"(
112 Notification call: 1234
113 Reply(1234): 5678
114 Call foo("abcd"): "efgh"
115 Reply("xyz"): error = 99: bad!
116 Call err("wxyz"): "boom!"
117 Notification exit: null
118 )";
119 EXPECT_EQ(trim(E.log()), trim(WantLog));
120 const char *WantOutput =
121 "Content-Length: 60\r\n\r\n"
122 R"({"id":42,"jsonrpc":"2.0","method":"echo call","params":1234})"
123 "Content-Length: 45\r\n\r\n"
124 R"({"id":"abcd","jsonrpc":"2.0","result":"efgh"})"
125 "Content-Length: 77\r\n\r\n"
126 R"({"error":{"code":88,"message":"trouble at mill"},"id":"wxyz","jsonrpc":"2.0"})";
127 EXPECT_EQ(output(), WantOutput);
128 EXPECT_EQ(trim(input_mirror()), trim(input()));
129 }
130
131 // Runs an Echo session using the "delimited" input and pretty-printed output
132 // that we use in lit tests.
133 TEST_F(JSONTransportTest, DelimitedPretty) {
134 auto T = transport(R"jsonrpc(
135 {"jsonrpc": "2.0", "method": "call", "params": 1234}
136 ---
137 {"jsonrpc": "2.0", "id": 1234, "result": 5678}
138 ---
139 {"jsonrpc": "2.0", "method": "foo", "id": "abcd", "params": "efgh"}
140 ---
141 {"jsonrpc": "2.0", "id": "xyz", "error": {"code": 99, "message": "bad!"}}
142 ---
143 {"jsonrpc": "2.0", "method": "err", "id": "wxyz", "params": "boom!"}
144 ---
145 {"jsonrpc": "2.0", "method": "exit"}
146 )jsonrpc",
147 /*Pretty=*/true, JSONStreamStyle::Delimited);
148 Echo E(*T);
149 auto Err = T->loop(E);
150 EXPECT_FALSE(bool(Err)) << toString(std::move(Err));
151
152 const char *WantLog = R"(
153 Notification call: 1234
154 Reply(1234): 5678
155 Call foo("abcd"): "efgh"
156 Reply("xyz"): error = 99: bad!
157 Call err("wxyz"): "boom!"
158 Notification exit: null
159 )";
160 EXPECT_EQ(trim(E.log()), trim(WantLog));
161 const char *WantOutput = "Content-Length: 77\r\n\r\n"
162 R"({
163 "id": 42,
164 "jsonrpc": "2.0",
165 "method": "echo call",
166 "params": 1234
167 })"
168 "Content-Length: 58\r\n\r\n"
169 R"({
170 "id": "abcd",
171 "jsonrpc": "2.0",
172 "result": "efgh"
173 })"
174 "Content-Length: 105\r\n\r\n"
175 R"({
176 "error": {
177 "code": 88,
178 "message": "trouble at mill"
179 },
180 "id": "wxyz",
181 "jsonrpc": "2.0"
182 })";
183 EXPECT_EQ(output(), WantOutput);
184 EXPECT_EQ(trim(input_mirror()), trim(input()));
185 }
186
187 // IO errors such as EOF ane reported.
188 // The only successful return from loop() is if a handler returned false.
189 TEST_F(JSONTransportTest, EndOfFile) {
190 auto T = transport("Content-Length: 52\r\n\r\n"
191 R"({"jsonrpc": "2.0", "method": "call", "params": 1234})",
192 /*Pretty=*/false, JSONStreamStyle::Standard);
193 Echo E(*T);
194 auto Err = T->loop(E);
195 EXPECT_EQ(trim(E.log()), "Notification call: 1234");
196 EXPECT_TRUE(bool(Err)); // Ran into EOF with no handler signalling done.
197 consumeError(std::move(Err));
198 EXPECT_EQ(trim(input_mirror()), trim(input()));
199 }
200
201 #endif
202
203 } // namespace
204 } // namespace clangd
205 } // namespace clang
clang::clangd::JSONStreamStyle
JSONStreamStyle
Definition: Transport.h:65
clang::clangd::MessageType::Log
A log message.
Protocol.h
clang::tidy::cppcoreguidelines::toString
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Definition: SpecialMemberFunctionsCheck.cpp:58
Transport.h
clang::clangd::TEST_F
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
Definition: BackgroundIndexTests.cpp:88
clang::clangd::ErrorCode
ErrorCode
Definition: Protocol.h:40
clang::clangd::log
void log(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:62
clang::clangd::toString
static const char * toString(OffsetEncoding OE)
Definition: Protocol.cpp:1031
clang::clangd::newJSONTransport
std::unique_ptr< Transport > newJSONTransport(std::FILE *In, llvm::raw_ostream &Out, llvm::raw_ostream *InMirror, bool Pretty, JSONStreamStyle Style)
Definition: JSONTransport.cpp:302
clang::clangd::Delimited
Definition: Transport.h:69
clang::clangd::CompletionItemKind::Method
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::Standard
Definition: Transport.h:67
E
const Expr * E
Definition: AvoidBindCheck.cpp:82