brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.5 KiB · 03f7870 Raw
165 lines · cpp
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-exception6//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 15namespace clang {16namespace clangd {17namespace {18 19using testing::ElementsAre;20using testing::HasSubstr;21using testing::IsEmpty;22using testing::UnorderedElementsAre;23 24// JSON-serializable type for testing.25struct Foo {26  int X;27  friend bool operator==(Foo A, Foo B) { return A.X == B.X; }28};29bool fromJSON(const llvm::json::Value &V, Foo &F, llvm::json::Path P) {30  return fromJSON(V, F.X, P.field("X"));31}32llvm::json::Value toJSON(const Foo &F) { return F.X; }33 34// Creates a Callback that writes its received value into an35// std::optional<Expected>.36template <typename T>37llvm::unique_function<void(llvm::Expected<T>)>38capture(std::optional<llvm::Expected<T>> &Out) {39  Out.reset();40  return [&Out](llvm::Expected<T> V) { Out.emplace(std::move(V)); };41}42 43struct 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 request52  }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 64TEST(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 logged116  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 128TEST(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} // namespace163} // namespace clangd164} // namespace clang165