18 #include "llvm/ADT/StringRef.h"
19 #include "llvm/Support/Error.h"
20 #include "llvm/Support/JSON.h"
21 #include "llvm/Testing/Support/SupportHelpers.h"
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
30 if (
const auto *O = arg.getAsObject()) {
31 if (
const auto Msg = O->getString(
"message"))
37 class LSPTest :
public ::testing::Test,
private clangd::Logger {
39 LSPTest() : LogSession(*this) {}
42 EXPECT_FALSE(Server.hasValue()) <<
"Already initialized";
46 ServerThread.emplace([&] { EXPECT_TRUE(Server->run()); });
47 Client.call(
"initialize", llvm::json::Object{});
53 Client.call(
"shutdown",
nullptr);
54 Client.notify(
"exit",
nullptr);
73 void log(Level L,
const llvm::formatv_object_base &
Message)
override {
74 raw_ostream::Colors
Color;
77 Color = raw_ostream::BLUE;
80 Color = raw_ostream::RED;
83 Color = raw_ostream::YELLOW;
86 std::lock_guard<std::mutex> Lock(LogMu);
87 (llvm::outs().changeColor(
Color) <<
Message <<
"\n").resetColor();
91 LoggingSession LogSession;
92 llvm::Optional<ClangdLSPServer> Server;
93 llvm::Optional<std::thread> ServerThread;
97 TEST_F(LSPTest, GoToDefinition) {
98 Annotations
Code(R
"cpp(
100 return n >= 2 ? ^fib(n - 1) + fib(n - 2) : 1;
103 auto &Client = start();
105 auto &Def = Client.
call(
"textDocument/definition",
107 {
"textDocument", Client.
documentID(
"foo.cpp")},
108 {
"position",
Code.point()},
110 llvm::json::Value Want = llvm::json::Array{llvm::json::Object{
111 {
"uri", Client.
uri(
"foo.cpp")}, {
"range",
Code.range()}}};
112 EXPECT_EQ(Def.takeValue(), Want);
115 TEST_F(LSPTest, Diagnostics) {
116 auto &Client = start();
117 Client.
didOpen(
"foo.cpp",
"void main(int, char**);");
119 llvm::ValueIs(testing::ElementsAre(
120 DiagMessage(
"'main' must return 'int' (fix available)"))));
122 Client.
didChange(
"foo.cpp",
"int x = \"42\";");
124 llvm::ValueIs(testing::ElementsAre(
125 DiagMessage(
"Cannot initialize a variable of type 'int' with "
126 "an lvalue of type 'const char [3]'"))));
129 EXPECT_THAT(Client.
diagnostics(
"foo.cpp"), llvm::ValueIs(testing::IsEmpty()));
132 TEST_F(LSPTest, DiagnosticsHeaderSaved) {
133 auto &Client = start();
134 Client.
didOpen(
"foo.cpp", R
"cpp(
139 llvm::ValueIs(testing::ElementsAre(
140 DiagMessage(
"'foo.h' file not found"),
141 DiagMessage(
"Use of undeclared identifier 'VAR'"))));
143 FS.
Files[
"foo.h"] =
"#define VAR original";
145 "textDocument/didSave",
146 llvm::json::Object{{
"textDocument", Client.
documentID(
"foo.h")}});
148 llvm::ValueIs(testing::ElementsAre(
149 DiagMessage(
"Use of undeclared identifier 'original'"))));
151 FS.
Files[
"foo.h"] =
"#define VAR changed";
153 "textDocument/didSave",
154 llvm::json::Object{{
"textDocument", Client.
documentID(
"foo.h")}});
157 llvm::ValueIs(testing::ElementsAre(
158 DiagMessage(
"Use of undeclared identifier 'changed'"))));
161 TEST_F(LSPTest, RecordsLatencies) {
163 auto &Client = start();
164 llvm::StringLiteral MethodName =
"method_name";
165 EXPECT_THAT(
Tracer.takeMetric(
"lsp_latency", MethodName), testing::SizeIs(0));
166 llvm::consumeError(Client.
call(MethodName, {}).
take().takeError());
168 EXPECT_THAT(
Tracer.takeMetric(
"lsp_latency", MethodName), testing::SizeIs(1));