clang-tools  12.0.0git
ClangdLSPServerTests.cpp
Go to the documentation of this file.
1 //===-- ClangdLSPServerTests.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 "Annotations.h"
10 #include "ClangdLSPServer.h"
11 #include "CodeComplete.h"
12 #include "LSPClient.h"
13 #include "Protocol.h"
14 #include "TestFS.h"
15 #include "refactor/Rename.h"
16 #include "support/Logger.h"
17 #include "support/TestTracer.h"
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"
24 
25 namespace clang {
26 namespace clangd {
27 namespace {
28 
29 MATCHER_P(DiagMessage, M, "") {
30  if (const auto *O = arg.getAsObject()) {
31  if (const auto Msg = O->getString("message"))
32  return *Msg == M;
33  }
34  return false;
35 }
36 
37 class LSPTest : public ::testing::Test, private clangd::Logger {
38 protected:
39  LSPTest() : LogSession(*this) {}
40 
41  LSPClient &start() {
42  EXPECT_FALSE(Server.hasValue()) << "Already initialized";
43  Server.emplace(Client.transport(), FS, CCOpts, RenameOpts,
44  /*CompileCommandsDir=*/llvm::None, /*UseDirBasedCDB=*/false,
45  /*ForcedOffsetEncoding=*/llvm::None, Opts);
46  ServerThread.emplace([&] { EXPECT_TRUE(Server->run()); });
47  Client.call("initialize", llvm::json::Object{});
48  return Client;
49  }
50 
51  void stop() {
52  assert(Server);
53  Client.call("shutdown", nullptr);
54  Client.notify("exit", nullptr);
55  Client.stop();
56  ServerThread->join();
57  Server.reset();
58  ServerThread.reset();
59  }
60 
61  ~LSPTest() {
62  if (Server)
63  stop();
64  }
65 
66  MockFS FS;
67  CodeCompleteOptions CCOpts;
68  RenameOptions RenameOpts;
69  ClangdServer::Options Opts = ClangdServer::optsForTest();
70 
71 private:
72  // Color logs so we can distinguish them from test output.
73  void log(Level L, const llvm::formatv_object_base &Message) override {
74  raw_ostream::Colors Color;
75  switch (L) {
76  case Level::Verbose:
77  Color = raw_ostream::BLUE;
78  break;
79  case Level::Error:
80  Color = raw_ostream::RED;
81  break;
82  default:
83  Color = raw_ostream::YELLOW;
84  break;
85  }
86  std::lock_guard<std::mutex> Lock(LogMu);
87  (llvm::outs().changeColor(Color) << Message << "\n").resetColor();
88  }
89  std::mutex LogMu;
90 
91  LoggingSession LogSession;
92  llvm::Optional<ClangdLSPServer> Server;
93  llvm::Optional<std::thread> ServerThread;
94  LSPClient Client;
95 };
96 
97 TEST_F(LSPTest, GoToDefinition) {
98  Annotations Code(R"cpp(
99  int [[fib]](int n) {
100  return n >= 2 ? ^fib(n - 1) + fib(n - 2) : 1;
101  }
102  )cpp");
103  auto &Client = start();
104  Client.didOpen("foo.cpp", Code.code());
105  auto &Def = Client.call("textDocument/definition",
106  llvm::json::Object{
107  {"textDocument", Client.documentID("foo.cpp")},
108  {"position", Code.point()},
109  });
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);
113 }
114 
115 TEST_F(LSPTest, Diagnostics) {
116  auto &Client = start();
117  Client.didOpen("foo.cpp", "void main(int, char**);");
118  EXPECT_THAT(Client.diagnostics("foo.cpp"),
119  llvm::ValueIs(testing::ElementsAre(
120  DiagMessage("'main' must return 'int' (fix available)"))));
121 
122  Client.didChange("foo.cpp", "int x = \"42\";");
123  EXPECT_THAT(Client.diagnostics("foo.cpp"),
124  llvm::ValueIs(testing::ElementsAre(
125  DiagMessage("Cannot initialize a variable of type 'int' with "
126  "an lvalue of type 'const char [3]'"))));
127 
128  Client.didClose("foo.cpp");
129  EXPECT_THAT(Client.diagnostics("foo.cpp"), llvm::ValueIs(testing::IsEmpty()));
130 }
131 
132 TEST_F(LSPTest, DiagnosticsHeaderSaved) {
133  auto &Client = start();
134  Client.didOpen("foo.cpp", R"cpp(
135  #include "foo.h"
136  int x = VAR;
137  )cpp");
138  EXPECT_THAT(Client.diagnostics("foo.cpp"),
139  llvm::ValueIs(testing::ElementsAre(
140  DiagMessage("'foo.h' file not found"),
141  DiagMessage("Use of undeclared identifier 'VAR'"))));
142  // Now create the header.
143  FS.Files["foo.h"] = "#define VAR original";
144  Client.notify(
145  "textDocument/didSave",
146  llvm::json::Object{{"textDocument", Client.documentID("foo.h")}});
147  EXPECT_THAT(Client.diagnostics("foo.cpp"),
148  llvm::ValueIs(testing::ElementsAre(
149  DiagMessage("Use of undeclared identifier 'original'"))));
150  // Now modify the header from within the "editor".
151  FS.Files["foo.h"] = "#define VAR changed";
152  Client.notify(
153  "textDocument/didSave",
154  llvm::json::Object{{"textDocument", Client.documentID("foo.h")}});
155  // Foo.cpp should be rebuilt with new diagnostics.
156  EXPECT_THAT(Client.diagnostics("foo.cpp"),
157  llvm::ValueIs(testing::ElementsAre(
158  DiagMessage("Use of undeclared identifier 'changed'"))));
159 }
160 
161 TEST_F(LSPTest, RecordsLatencies) {
162  trace::TestTracer Tracer;
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());
167  stop();
168  EXPECT_THAT(Tracer.takeMetric("lsp_latency", MethodName), testing::SizeIs(1));
169 }
170 } // namespace
171 } // namespace clangd
172 } // namespace clang
std::string Code
MATCHER_P(Named, N, "")
MockFS FS
ClangdServer::Options Opts
static Options optsForTest()
Documents should not be synced at all.
TEST_F(BackgroundIndexTest, NoCrashOnErrorFile)
void log(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:62
RenameOptions RenameOpts
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
std::unique_ptr< trace::EventTracer > Tracer
Definition: TraceTests.cpp:163
CodeCompleteOptions CCOpts