clang-tools  14.0.0git
LSPBinderTests.cpp
Go to the documentation of this file.
1 //===-- LSPBinderTests.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 "LSPBinder.h"
10 #include "llvm/Testing/Support/Error.h"
11 #include "gmock/gmock.h"
12 #include "gtest/gtest.h"
13 
14 namespace clang {
15 namespace clangd {
16 namespace {
17 
18 using testing::ElementsAre;
19 using testing::HasSubstr;
20 using testing::IsEmpty;
21 using testing::UnorderedElementsAre;
22 
23 // JSON-serializable type for testing.
24 struct Foo {
25  int X;
26  friend bool operator==(Foo A, Foo B) { return A.X == B.X; }
27 };
28 bool fromJSON(const llvm::json::Value &V, Foo &F, llvm::json::Path P) {
29  return fromJSON(V, F.X, P.field("X"));
30 }
31 llvm::json::Value toJSON(const Foo &F) { return F.X; }
32 
33 // Creates a Callback that writes its received value into an Optional<Expected>.
34 template <typename T>
35 llvm::unique_function<void(llvm::Expected<T>)>
36 capture(llvm::Optional<llvm::Expected<T>> &Out) {
37  Out.reset();
38  return [&Out](llvm::Expected<T> V) { Out.emplace(std::move(V)); };
39 }
40 
41 struct OutgoingRecorder : public LSPBinder::RawOutgoing {
42  llvm::StringMap<std::vector<llvm::json::Value>> Received;
43 
44  void callMethod(llvm::StringRef Method, llvm::json::Value Params,
45  Callback<llvm::json::Value> Reply) override {
46  Received[Method].push_back(Params);
47  if (Method == "fail")
48  return Reply(error("Params={0}", Params));
49  Reply(Params); // echo back the request
50  }
51  void notify(llvm::StringRef Method, llvm::json::Value Params) override {
52  Received[Method].push_back(std::move(Params));
53  }
54 
55  std::vector<llvm::json::Value> take(llvm::StringRef Method) {
56  std::vector<llvm::json::Value> Result = Received.lookup(Method);
57  Received.erase(Method);
58  return Result;
59  }
60 };
61 
62 TEST(LSPBinderTest, IncomingCalls) {
63  LSPBinder::RawHandlers RawHandlers;
64  OutgoingRecorder RawOutgoing;
65  LSPBinder Binder{RawHandlers, RawOutgoing};
66  struct Handler {
67  void plusOne(const Foo &Params, Callback<Foo> Reply) {
68  Reply(Foo{Params.X + 1});
69  }
70  void fail(const Foo &Params, Callback<Foo> Reply) {
71  Reply(error("X={0}", Params.X));
72  }
73  void notify(const Foo &Params) {
74  LastNotify = Params.X;
75  ++NotifyCount;
76  }
77  int LastNotify = -1;
78  int NotifyCount = 0;
79  };
80 
81  Handler H;
82  Binder.method("plusOne", &H, &Handler::plusOne);
83  Binder.method("fail", &H, &Handler::fail);
84  Binder.notification("notify", &H, &Handler::notify);
85  Binder.command("cmdPlusOne", &H, &Handler::plusOne);
86  ASSERT_THAT(RawHandlers.MethodHandlers.keys(),
87  UnorderedElementsAre("plusOne", "fail"));
88  ASSERT_THAT(RawHandlers.NotificationHandlers.keys(),
89  UnorderedElementsAre("notify"));
90  ASSERT_THAT(RawHandlers.CommandHandlers.keys(),
91  UnorderedElementsAre("cmdPlusOne"));
92  llvm::Optional<llvm::Expected<llvm::json::Value>> Reply;
93 
94  auto &RawPlusOne = RawHandlers.MethodHandlers["plusOne"];
95  RawPlusOne(1, capture(Reply));
96  ASSERT_TRUE(Reply.hasValue());
97  EXPECT_THAT_EXPECTED(Reply.getValue(), llvm::HasValue(2));
98  RawPlusOne("foo", capture(Reply));
99  ASSERT_TRUE(Reply.hasValue());
100  EXPECT_THAT_EXPECTED(
101  Reply.getValue(),
102  llvm::FailedWithMessage(
103  HasSubstr("failed to decode plusOne request: expected integer")));
104 
105  auto &RawFail = RawHandlers.MethodHandlers["fail"];
106  RawFail(2, capture(Reply));
107  ASSERT_TRUE(Reply.hasValue());
108  EXPECT_THAT_EXPECTED(Reply.getValue(), llvm::FailedWithMessage("X=2"));
109 
110  auto &RawNotify = RawHandlers.NotificationHandlers["notify"];
111  RawNotify(42);
112  EXPECT_EQ(H.LastNotify, 42);
113  EXPECT_EQ(H.NotifyCount, 1);
114  RawNotify("hi"); // invalid, will be logged
115  EXPECT_EQ(H.LastNotify, 42);
116  EXPECT_EQ(H.NotifyCount, 1);
117 
118  auto &RawCmdPlusOne = RawHandlers.CommandHandlers["cmdPlusOne"];
119  RawCmdPlusOne(1, capture(Reply));
120  ASSERT_TRUE(Reply.hasValue());
121  EXPECT_THAT_EXPECTED(Reply.getValue(), llvm::HasValue(2));
122 
123  // None of this generated any outgoing traffic.
124  EXPECT_THAT(RawOutgoing.Received, IsEmpty());
125 }
126 
127 TEST(LSPBinderTest, OutgoingCalls) {
128  LSPBinder::RawHandlers RawHandlers;
129  OutgoingRecorder RawOutgoing;
130  LSPBinder Binder{RawHandlers, RawOutgoing};
131 
132  LSPBinder::OutgoingMethod<Foo, Foo> Echo;
133  Echo = Binder.outgoingMethod("echo");
134  LSPBinder::OutgoingMethod<Foo, std::string> WrongSignature;
135  WrongSignature = Binder.outgoingMethod("wrongSignature");
136  LSPBinder::OutgoingMethod<Foo, Foo> Fail;
137  Fail = Binder.outgoingMethod("fail");
138 
139  llvm::Optional<llvm::Expected<Foo>> Reply;
140  Echo(Foo{2}, capture(Reply));
141  EXPECT_THAT(RawOutgoing.take("echo"), ElementsAre(llvm::json::Value(2)));
142  ASSERT_TRUE(Reply.hasValue());
143  EXPECT_THAT_EXPECTED(Reply.getValue(), llvm::HasValue(Foo{2}));
144 
145  // JSON response is integer, can't be parsed as string.
146  llvm::Optional<llvm::Expected<std::string>> WrongTypeReply;
147  WrongSignature(Foo{2}, capture(WrongTypeReply));
148  EXPECT_THAT(RawOutgoing.take("wrongSignature"),
149  ElementsAre(llvm::json::Value(2)));
150  ASSERT_TRUE(Reply.hasValue());
151  EXPECT_THAT_EXPECTED(WrongTypeReply.getValue(),
152  llvm::FailedWithMessage(
153  HasSubstr("failed to decode wrongSignature reply")));
154 
155  Fail(Foo{2}, capture(Reply));
156  EXPECT_THAT(RawOutgoing.take("fail"), ElementsAre(llvm::json::Value(2)));
157  ASSERT_TRUE(Reply.hasValue());
158  EXPECT_THAT_EXPECTED(Reply.getValue(), llvm::FailedWithMessage("Params=2"));
159 }
160 
161 } // namespace
162 } // namespace clangd
163 } // namespace clang
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:751
clang::clangd::error
llvm::Error error(std::error_code EC, const char *Fmt, Ts &&... Vals)
Definition: Logger.h:80
clang::clangd::X
static URISchemeRegistry::Add< TestScheme > X(TestScheme::Scheme, "Test schema")
LSPBinder.h
ns1::ns2::A
@ A
Definition: CategoricalFeature.h:3
Foo
Definition: sample.h:4
clang::clangd::toJSON
llvm::json::Value toJSON(const FuzzyFindRequest &Request)
Definition: Index.cpp:49
clang::clangd::fromJSON
bool fromJSON(const llvm::json::Value &Parameters, FuzzyFindRequest &Request, llvm::json::Path P)
Definition: Index.cpp:34
Received
llvm::StringMap< std::vector< llvm::json::Value > > Received
Definition: LSPBinderTests.cpp:42
clang::clangd::CompletionItemKind::Method
@ Method
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::operator==
bool operator==(const Inclusion &LHS, const Inclusion &RHS)
Definition: Headers.cpp:286
ns1::ns2::B
@ B
Definition: CategoricalFeature.h:3
Out
CompiledFragmentImpl & Out
Definition: ConfigCompile.cpp:100
clang::clangd::HasValue
OptionalMatcher< InnerMatcher > HasValue(const InnerMatcher &inner_matcher)
Definition: clangd/unittests/Matchers.h:194
Value
static constexpr bool Value
Definition: SuspiciousCallArgumentCheck.cpp:72
Path
std::vector< HeaderHandle > Path
Definition: PreprocessorTracker.cpp:525