llvm-project

Форк
0
164 строки · 5.5 Кб
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
#include <optional>
14

15
namespace clang {
16
namespace clangd {
17
namespace {
18

19
using testing::ElementsAre;
20
using testing::HasSubstr;
21
using testing::IsEmpty;
22
using testing::UnorderedElementsAre;
23

24
// JSON-serializable type for testing.
25
struct Foo {
26
  int X;
27
  friend bool operator==(Foo A, Foo B) { return A.X == B.X; }
28
};
29
bool fromJSON(const llvm::json::Value &V, Foo &F, llvm::json::Path P) {
30
  return fromJSON(V, F.X, P.field("X"));
31
}
32
llvm::json::Value toJSON(const Foo &F) { return F.X; }
33

34
// Creates a Callback that writes its received value into an
35
// std::optional<Expected>.
36
template <typename T>
37
llvm::unique_function<void(llvm::Expected<T>)>
38
capture(std::optional<llvm::Expected<T>> &Out) {
39
  Out.reset();
40
  return [&Out](llvm::Expected<T> V) { Out.emplace(std::move(V)); };
41
}
42

43
struct OutgoingRecorder : public LSPBinder::RawOutgoing {
44
  llvm::StringMap<std::vector<llvm::json::Value>> Received;
45

46
  void callMethod(llvm::StringRef Method, llvm::json::Value Params,
47
                  Callback<llvm::json::Value> Reply) override {
48
    Received[Method].push_back(Params);
49
    if (Method == "fail")
50
      return Reply(error("Params={0}", Params));
51
    Reply(Params); // echo back the request
52
  }
53
  void notify(llvm::StringRef Method, llvm::json::Value Params) override {
54
    Received[Method].push_back(std::move(Params));
55
  }
56

57
  std::vector<llvm::json::Value> take(llvm::StringRef Method) {
58
    std::vector<llvm::json::Value> Result = Received.lookup(Method);
59
    Received.erase(Method);
60
    return Result;
61
  }
62
};
63

64
TEST(LSPBinderTest, IncomingCalls) {
65
  LSPBinder::RawHandlers RawHandlers;
66
  OutgoingRecorder RawOutgoing;
67
  LSPBinder Binder{RawHandlers, RawOutgoing};
68
  struct Handler {
69
    void plusOne(const Foo &Params, Callback<Foo> Reply) {
70
      Reply(Foo{Params.X + 1});
71
    }
72
    void fail(const Foo &Params, Callback<Foo> Reply) {
73
      Reply(error("X={0}", Params.X));
74
    }
75
    void notify(const Foo &Params) {
76
      LastNotify = Params.X;
77
      ++NotifyCount;
78
    }
79
    int LastNotify = -1;
80
    int NotifyCount = 0;
81
  };
82

83
  Handler H;
84
  Binder.method("plusOne", &H, &Handler::plusOne);
85
  Binder.method("fail", &H, &Handler::fail);
86
  Binder.notification("notify", &H, &Handler::notify);
87
  Binder.command("cmdPlusOne", &H, &Handler::plusOne);
88
  ASSERT_THAT(RawHandlers.MethodHandlers.keys(),
89
              UnorderedElementsAre("plusOne", "fail"));
90
  ASSERT_THAT(RawHandlers.NotificationHandlers.keys(),
91
              UnorderedElementsAre("notify"));
92
  ASSERT_THAT(RawHandlers.CommandHandlers.keys(),
93
              UnorderedElementsAre("cmdPlusOne"));
94
  std::optional<llvm::Expected<llvm::json::Value>> Reply;
95

96
  auto &RawPlusOne = RawHandlers.MethodHandlers["plusOne"];
97
  RawPlusOne(1, capture(Reply));
98
  ASSERT_TRUE(Reply.has_value());
99
  EXPECT_THAT_EXPECTED(*Reply, llvm::HasValue(2));
100
  RawPlusOne("foo", capture(Reply));
101
  ASSERT_TRUE(Reply.has_value());
102
  EXPECT_THAT_EXPECTED(
103
      *Reply, llvm::FailedWithMessage(HasSubstr(
104
                  "failed to decode plusOne request: expected integer")));
105

106
  auto &RawFail = RawHandlers.MethodHandlers["fail"];
107
  RawFail(2, capture(Reply));
108
  ASSERT_TRUE(Reply.has_value());
109
  EXPECT_THAT_EXPECTED(*Reply, llvm::FailedWithMessage("X=2"));
110

111
  auto &RawNotify = RawHandlers.NotificationHandlers["notify"];
112
  RawNotify(42);
113
  EXPECT_EQ(H.LastNotify, 42);
114
  EXPECT_EQ(H.NotifyCount, 1);
115
  RawNotify("hi"); // invalid, will be logged
116
  EXPECT_EQ(H.LastNotify, 42);
117
  EXPECT_EQ(H.NotifyCount, 1);
118

119
  auto &RawCmdPlusOne = RawHandlers.CommandHandlers["cmdPlusOne"];
120
  RawCmdPlusOne(1, capture(Reply));
121
  ASSERT_TRUE(Reply.has_value());
122
  EXPECT_THAT_EXPECTED(*Reply, llvm::HasValue(2));
123

124
  // None of this generated any outgoing traffic.
125
  EXPECT_THAT(RawOutgoing.Received, IsEmpty());
126
}
127

128
TEST(LSPBinderTest, OutgoingCalls) {
129
  LSPBinder::RawHandlers RawHandlers;
130
  OutgoingRecorder RawOutgoing;
131
  LSPBinder Binder{RawHandlers, RawOutgoing};
132

133
  LSPBinder::OutgoingMethod<Foo, Foo> Echo;
134
  Echo = Binder.outgoingMethod("echo");
135
  LSPBinder::OutgoingMethod<Foo, std::string> WrongSignature;
136
  WrongSignature = Binder.outgoingMethod("wrongSignature");
137
  LSPBinder::OutgoingMethod<Foo, Foo> Fail;
138
  Fail = Binder.outgoingMethod("fail");
139

140
  std::optional<llvm::Expected<Foo>> Reply;
141
  Echo(Foo{2}, capture(Reply));
142
  EXPECT_THAT(RawOutgoing.take("echo"), ElementsAre(llvm::json::Value(2)));
143
  ASSERT_TRUE(Reply.has_value());
144
  EXPECT_THAT_EXPECTED(*Reply, llvm::HasValue(Foo{2}));
145

146
  // JSON response is integer, can't be parsed as string.
147
  std::optional<llvm::Expected<std::string>> WrongTypeReply;
148
  WrongSignature(Foo{2}, capture(WrongTypeReply));
149
  EXPECT_THAT(RawOutgoing.take("wrongSignature"),
150
              ElementsAre(llvm::json::Value(2)));
151
  ASSERT_TRUE(Reply.has_value());
152
  EXPECT_THAT_EXPECTED(*WrongTypeReply,
153
                       llvm::FailedWithMessage(
154
                           HasSubstr("failed to decode wrongSignature reply")));
155

156
  Fail(Foo{2}, capture(Reply));
157
  EXPECT_THAT(RawOutgoing.take("fail"), ElementsAre(llvm::json::Value(2)));
158
  ASSERT_TRUE(Reply.has_value());
159
  EXPECT_THAT_EXPECTED(*Reply, llvm::FailedWithMessage("Params=2"));
160
}
161

162
} // namespace
163
} // namespace clangd
164
} // namespace clang
165

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.