817 lines · cpp
1//===----------------------------------------------------------------------===//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 "lldb/Host/JSONTransport.h"10#include "TestingSupport/Host/JSONTransportTestUtilities.h"11#include "TestingSupport/Host/PipeTestUtilities.h"12#include "TestingSupport/SubsystemRAII.h"13#include "lldb/Host/File.h"14#include "lldb/Host/MainLoop.h"15#include "lldb/Host/MainLoopBase.h"16#include "lldb/Utility/Log.h"17#include "llvm/ADT/StringRef.h"18#include "llvm/Support/Error.h"19#include "llvm/Support/ErrorHandling.h"20#include "llvm/Support/FormatVariadic.h"21#include "llvm/Support/JSON.h"22#include "llvm/Support/raw_ostream.h"23#include "llvm/Testing/Support/Error.h"24#include "gmock/gmock.h"25#include "gtest/gtest.h"26#include <chrono>27#include <cstddef>28#include <memory>29#include <optional>30#include <string>31#include <system_error>32 33using namespace llvm;34using namespace lldb_private;35using namespace lldb_private::transport;36using testing::_;37using testing::HasSubstr;38using testing::InSequence;39using testing::Ref;40 41namespace llvm::json {42static bool fromJSON(const Value &V, Value &T, Path P) {43 T = V;44 return true;45}46} // namespace llvm::json47 48namespace {49 50namespace test_protocol {51 52struct Request {53 int id = 0;54 std::string name;55 std::optional<json::Value> params;56};57json::Value toJSON(const Request &T) {58 return json::Object{{"name", T.name}, {"id", T.id}, {"params", T.params}};59}60bool fromJSON(const json::Value &V, Request &T, json::Path P) {61 json::ObjectMapper O(V, P);62 return O && O.map("name", T.name) && O.map("id", T.id) &&63 O.map("params", T.params);64}65bool operator==(const Request &a, const Request &b) {66 return a.name == b.name && a.id == b.id && a.params == b.params;67}68inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Request &V) {69 OS << toJSON(V);70 return OS;71}72void PrintTo(const Request &message, std::ostream *os) {73 std::string O;74 llvm::raw_string_ostream OS(O);75 OS << message;76 *os << O;77}78 79struct Response {80 int id = 0;81 int errorCode = 0;82 std::optional<json::Value> result;83};84json::Value toJSON(const Response &T) {85 return json::Object{86 {"id", T.id}, {"errorCode", T.errorCode}, {"result", T.result}};87}88bool fromJSON(const json::Value &V, Response &T, json::Path P) {89 json::ObjectMapper O(V, P);90 return O && O.map("id", T.id) && O.mapOptional("errorCode", T.errorCode) &&91 O.map("result", T.result);92}93bool operator==(const Response &a, const Response &b) {94 return a.id == b.id && a.errorCode == b.errorCode && a.result == b.result;95}96inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Response &V) {97 OS << toJSON(V);98 return OS;99}100void PrintTo(const Response &message, std::ostream *os) {101 std::string O;102 llvm::raw_string_ostream OS(O);103 OS << message;104 *os << O;105}106 107struct Event {108 std::string name;109 std::optional<json::Value> params;110};111json::Value toJSON(const Event &T) {112 return json::Object{{"name", T.name}, {"params", T.params}};113}114bool fromJSON(const json::Value &V, Event &T, json::Path P) {115 json::ObjectMapper O(V, P);116 return O && O.map("name", T.name) && O.map("params", T.params);117}118bool operator==(const Event &a, const Event &b) { return a.name == b.name; }119inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Event &V) {120 OS << toJSON(V);121 return OS;122}123void PrintTo(const Event &message, std::ostream *os) {124 std::string O;125 llvm::raw_string_ostream OS(O);126 OS << message;127 *os << O;128}129 130using Message = std::variant<Request, Response, Event>;131json::Value toJSON(const Message &msg) {132 return std::visit([](const auto &msg) { return toJSON(msg); }, msg);133}134bool fromJSON(const json::Value &V, Message &msg, json::Path P) {135 const json::Object *O = V.getAsObject();136 if (!O) {137 P.report("expected object");138 return false;139 }140 141 if (O->find("id") == O->end()) {142 Event E;143 if (!fromJSON(V, E, P))144 return false;145 146 msg = std::move(E);147 return true;148 }149 150 if (O->get("name")) {151 Request R;152 if (!fromJSON(V, R, P))153 return false;154 155 msg = std::move(R);156 return true;157 }158 159 Response R;160 if (!fromJSON(V, R, P))161 return false;162 163 msg = std::move(R);164 return true;165}166 167struct MyFnParams {168 int a = 0;169 int b = 0;170};171json::Value toJSON(const MyFnParams &T) {172 return json::Object{{"a", T.a}, {"b", T.b}};173}174bool fromJSON(const json::Value &V, MyFnParams &T, json::Path P) {175 json::ObjectMapper O(V, P);176 return O && O.map("a", T.a) && O.map("b", T.b);177}178 179struct MyFnResult {180 int c = 0;181};182json::Value toJSON(const MyFnResult &T) { return json::Object{{"c", T.c}}; }183bool fromJSON(const json::Value &V, MyFnResult &T, json::Path P) {184 json::ObjectMapper O(V, P);185 return O && O.map("c", T.c);186}187 188struct ProtoDesc {189 using Id = int;190 using Req = Request;191 using Resp = Response;192 using Evt = Event;193 194 static inline Id InitialId() { return 0; }195 static inline Req Make(Id id, llvm::StringRef method,196 std::optional<llvm::json::Value> params) {197 return Req{id, method.str(), params};198 }199 static inline Evt Make(llvm::StringRef method,200 std::optional<llvm::json::Value> params) {201 return Evt{method.str(), params};202 }203 static inline Resp Make(Req req, llvm::Error error) {204 Resp resp;205 resp.id = req.id;206 llvm::handleAllErrors(207 std::move(error), [&](const llvm::ErrorInfoBase &err) {208 std::error_code cerr = err.convertToErrorCode();209 resp.errorCode =210 cerr == llvm::inconvertibleErrorCode() ? 1 : cerr.value();211 resp.result = err.message();212 });213 return resp;214 }215 static inline Resp Make(Req req, std::optional<llvm::json::Value> result) {216 return Resp{req.id, 0, std::move(result)};217 }218 static inline Id KeyFor(Resp r) { return r.id; }219 static inline std::string KeyFor(Req r) { return r.name; }220 static inline std::string KeyFor(Evt e) { return e.name; }221 static inline std::optional<llvm::json::Value> Extract(Req r) {222 return r.params;223 }224 static inline llvm::Expected<llvm::json::Value> Extract(Resp r) {225 if (r.errorCode != 0)226 return llvm::createStringError(227 std::error_code(r.errorCode, std::generic_category()),228 r.result && r.result->getAsString() ? *r.result->getAsString()229 : "no-message");230 return r.result;231 }232 static inline std::optional<llvm::json::Value> Extract(Evt e) {233 return e.params;234 }235};236 237using Transport = TestTransport<ProtoDesc>;238using Binder = lldb_private::transport::Binder<ProtoDesc>;239using MessageHandler = MockMessageHandler<ProtoDesc>;240 241} // namespace test_protocol242 243template <typename T> class JSONTransportTest : public PipePairTest {244protected:245 SubsystemRAII<FileSystem> subsystems;246 247 test_protocol::MessageHandler message_handler;248 std::unique_ptr<T> transport;249 MainLoop loop;250 251 void SetUp() override {252 PipePairTest::SetUp();253 transport = std::make_unique<T>(254 std::make_shared<NativeFile>(input.GetReadFileDescriptor(),255 File::eOpenOptionReadOnly,256 NativeFile::Unowned),257 std::make_shared<NativeFile>(output.GetWriteFileDescriptor(),258 File::eOpenOptionWriteOnly,259 NativeFile::Unowned));260 }261 262 /// Run the transport MainLoop and return any messages received.263 Error264 Run(bool close_input = true,265 std::chrono::milliseconds timeout = std::chrono::milliseconds(5000)) {266 if (close_input) {267 input.CloseWriteFileDescriptor();268 EXPECT_CALL(message_handler, OnClosed()).WillOnce([this]() {269 loop.RequestTermination();270 });271 }272 bool addition_succeeded = loop.AddCallback(273 [](MainLoopBase &loop) {274 loop.RequestTermination();275 FAIL() << "timeout";276 },277 timeout);278 EXPECT_TRUE(addition_succeeded);279 auto handle = transport->RegisterMessageHandler(loop, message_handler);280 if (!handle)281 return handle.takeError();282 283 return loop.Run().takeError();284 }285 286 template <typename... Ts> void Write(Ts... args) {287 std::string message;288 for (const auto &arg : {args...})289 message += Encode(arg);290 EXPECT_THAT_EXPECTED(input.Write(message.data(), message.size()),291 Succeeded());292 }293 294 virtual std::string Encode(const json::Value &) = 0;295};296 297class TestHTTPDelimitedJSONTransport final298 : public HTTPDelimitedJSONTransport<test_protocol::ProtoDesc> {299public:300 using HTTPDelimitedJSONTransport::HTTPDelimitedJSONTransport;301 302 void Log(llvm::StringRef message) override {303 log_messages.emplace_back(message);304 }305 306 std::vector<std::string> log_messages;307};308 309class HTTPDelimitedJSONTransportTest310 : public JSONTransportTest<TestHTTPDelimitedJSONTransport> {311public:312 using JSONTransportTest::JSONTransportTest;313 314 std::string Encode(const json::Value &V) override {315 std::string msg;316 raw_string_ostream OS(msg);317 OS << formatv("{0}", V);318 return formatv("Content-Length: {0}\r\nContent-type: "319 "text/json\r\n\r\n{1}",320 msg.size(), msg)321 .str();322 }323};324 325class TestJSONRPCTransport final326 : public JSONRPCTransport<test_protocol::ProtoDesc> {327public:328 using JSONRPCTransport::JSONRPCTransport;329 330 void Log(llvm::StringRef message) override {331 log_messages.emplace_back(message);332 }333 334 std::vector<std::string> log_messages;335};336 337class JSONRPCTransportTest : public JSONTransportTest<TestJSONRPCTransport> {338public:339 using JSONTransportTest::JSONTransportTest;340 341 std::string Encode(const json::Value &V) override {342 std::string msg;343 raw_string_ostream OS(msg);344 OS << formatv("{0}\n", V);345 return msg;346 }347};348 349class TransportBinderTest : public testing::Test {350protected:351 SubsystemRAII<FileSystem> subsystems;352 353 std::unique_ptr<test_protocol::Transport> to_remote;354 std::unique_ptr<test_protocol::Transport> from_remote;355 std::unique_ptr<test_protocol::Binder> binder;356 test_protocol::MessageHandler remote;357 MainLoop loop;358 359 void SetUp() override {360 std::tie(to_remote, from_remote) = test_protocol::Transport::createPair();361 binder = std::make_unique<test_protocol::Binder>(*to_remote);362 363 auto binder_handle = to_remote->RegisterMessageHandler(loop, remote);364 EXPECT_THAT_EXPECTED(binder_handle, Succeeded());365 366 auto remote_handle = from_remote->RegisterMessageHandler(loop, *binder);367 EXPECT_THAT_EXPECTED(remote_handle, Succeeded());368 }369 370 void Run() {371 bool addition_succeeded =372 loop.AddPendingCallback([](auto &loop) { loop.RequestTermination(); });373 EXPECT_TRUE(addition_succeeded);374 EXPECT_THAT_ERROR(loop.Run().takeError(), Succeeded());375 }376};377 378} // namespace379 380// Failing on Windows, see https://github.com/llvm/llvm-project/issues/153446.381#ifndef _WIN32382using namespace test_protocol;383 384TEST_F(HTTPDelimitedJSONTransportTest, MalformedRequests) {385 std::string malformed_header =386 "COnTent-LenGth: -1\r\nContent-Type: text/json\r\n\r\nnotjosn";387 ASSERT_THAT_EXPECTED(388 input.Write(malformed_header.data(), malformed_header.size()),389 Succeeded());390 391 EXPECT_CALL(message_handler, OnError(_)).WillOnce([](llvm::Error err) {392 ASSERT_THAT_ERROR(std::move(err),393 FailedWithMessage("invalid content length: -1"));394 });395 ASSERT_THAT_ERROR(Run(), Succeeded());396}397 398TEST_F(HTTPDelimitedJSONTransportTest, Read) {399 Write(Request{6, "foo", std::nullopt});400 EXPECT_CALL(message_handler, Received(Request{6, "foo", std::nullopt}));401 ASSERT_THAT_ERROR(Run(), Succeeded());402}403 404TEST_F(HTTPDelimitedJSONTransportTest, ReadMultipleMessagesInSingleWrite) {405 InSequence seq;406 Write(407 Message{408 Request{6, "one", std::nullopt},409 },410 Message{411 test_protocol::Event{"two", std::nullopt},412 },413 Message{414 Response{2, 0, std::nullopt},415 });416 EXPECT_CALL(message_handler, Received(Request{6, "one", std::nullopt}));417 EXPECT_CALL(message_handler,418 Received(test_protocol::Event{"two", std::nullopt}));419 EXPECT_CALL(message_handler, Received(Response{2, 0, std::nullopt}));420 ASSERT_THAT_ERROR(Run(), Succeeded());421}422 423TEST_F(HTTPDelimitedJSONTransportTest, ReadAcrossMultipleChunks) {424 std::string long_str = std::string(425 HTTPDelimitedJSONTransport<test_protocol::ProtoDesc>::kReadBufferSize * 2,426 'x');427 Write(Request{5, long_str, std::nullopt});428 EXPECT_CALL(message_handler, Received(Request{5, long_str, std::nullopt}));429 ASSERT_THAT_ERROR(Run(), Succeeded());430}431 432TEST_F(HTTPDelimitedJSONTransportTest, ReadPartialMessage) {433 std::string message = Encode(Request{5, "foo", std::nullopt});434 auto split_at = message.size() / 2;435 std::string part1 = message.substr(0, split_at);436 std::string part2 = message.substr(split_at);437 438 EXPECT_CALL(message_handler, Received(Request{5, "foo", std::nullopt}));439 440 ASSERT_THAT_EXPECTED(input.Write(part1.data(), part1.size()), Succeeded());441 bool addition_succeeded = loop.AddPendingCallback(442 [](MainLoopBase &loop) { loop.RequestTermination(); });443 EXPECT_TRUE(addition_succeeded);444 ASSERT_THAT_ERROR(Run(/*close_stdin=*/false), Succeeded());445 ASSERT_THAT_EXPECTED(input.Write(part2.data(), part2.size()), Succeeded());446 input.CloseWriteFileDescriptor();447 ASSERT_THAT_ERROR(Run(), Succeeded());448}449 450TEST_F(HTTPDelimitedJSONTransportTest, ReadWithZeroByteWrites) {451 std::string message = Encode(Request{6, "foo", std::nullopt});452 auto split_at = message.size() / 2;453 std::string part1 = message.substr(0, split_at);454 std::string part2 = message.substr(split_at);455 456 EXPECT_CALL(message_handler, Received(Request{6, "foo", std::nullopt}));457 458 ASSERT_THAT_EXPECTED(input.Write(part1.data(), part1.size()), Succeeded());459 460 // Run the main loop once for the initial read.461 bool addition_succeeded = loop.AddPendingCallback(462 [](MainLoopBase &loop) { loop.RequestTermination(); });463 EXPECT_TRUE(addition_succeeded);464 ASSERT_THAT_ERROR(Run(/*close_stdin=*/false), Succeeded());465 466 // zero-byte write.467 ASSERT_THAT_EXPECTED(input.Write(part1.data(), 0),468 Succeeded()); // zero-byte write.469 addition_succeeded = loop.AddPendingCallback(470 [](MainLoopBase &loop) { loop.RequestTermination(); });471 EXPECT_TRUE(addition_succeeded);472 ASSERT_THAT_ERROR(Run(/*close_stdin=*/false), Succeeded());473 474 // Write the remaining part of the message.475 ASSERT_THAT_EXPECTED(input.Write(part2.data(), part2.size()), Succeeded());476 ASSERT_THAT_ERROR(Run(), Succeeded());477}478 479TEST_F(HTTPDelimitedJSONTransportTest, ReadWithEOF) {480 ASSERT_THAT_ERROR(Run(), Succeeded());481}482 483TEST_F(HTTPDelimitedJSONTransportTest, ReaderWithUnhandledData) {484 std::string json = R"json({"str": "foo"})json";485 std::string message =486 formatv("Content-Length: {0}\r\nContent-type: text/json\r\n\r\n{1}",487 json.size(), json)488 .str();489 490 EXPECT_CALL(message_handler, OnError(_)).WillOnce([](llvm::Error err) {491 // The error should indicate that there are unhandled contents.492 ASSERT_THAT_ERROR(std::move(err),493 Failed<TransportUnhandledContentsError>());494 });495 496 // Write an incomplete message and close the handle.497 ASSERT_THAT_EXPECTED(input.Write(message.data(), message.size() - 1),498 Succeeded());499 ASSERT_THAT_ERROR(Run(), Succeeded());500}501 502TEST_F(HTTPDelimitedJSONTransportTest, InvalidTransport) {503 transport =504 std::make_unique<TestHTTPDelimitedJSONTransport>(nullptr, nullptr);505 ASSERT_THAT_ERROR(Run(/*close_input=*/false),506 FailedWithMessage("IO object is not valid."));507}508 509TEST_F(HTTPDelimitedJSONTransportTest, Write) {510 ASSERT_THAT_ERROR(transport->Send(Request{7, "foo", std::nullopt}),511 Succeeded());512 ASSERT_THAT_ERROR(transport->Send(Response{5, 0, "bar"}), Succeeded());513 ASSERT_THAT_ERROR(transport->Send(test_protocol::Event{"baz", std::nullopt}),514 Succeeded());515 output.CloseWriteFileDescriptor();516 char buf[1024];517 Expected<size_t> bytes_read =518 output.Read(buf, sizeof(buf), std::chrono::milliseconds(1));519 ASSERT_THAT_EXPECTED(bytes_read, Succeeded());520 ASSERT_EQ(StringRef(buf, *bytes_read),521 StringRef("Content-Length: 35\r\n\r\n"522 R"({"id":7,"name":"foo","params":null})"523 "Content-Length: 37\r\n\r\n"524 R"({"errorCode":0,"id":5,"result":"bar"})"525 "Content-Length: 28\r\n\r\n"526 R"({"name":"baz","params":null})"));527}528 529TEST_F(JSONRPCTransportTest, MalformedRequests) {530 std::string malformed_header = "notjson\n";531 ASSERT_THAT_EXPECTED(532 input.Write(malformed_header.data(), malformed_header.size()),533 Succeeded());534 EXPECT_CALL(message_handler, OnError(_)).WillOnce([](llvm::Error err) {535 ASSERT_THAT_ERROR(std::move(err),536 FailedWithMessage(HasSubstr("Invalid JSON value")));537 });538 ASSERT_THAT_ERROR(Run(), Succeeded());539}540 541TEST_F(JSONRPCTransportTest, Read) {542 Write(Message{Request{1, "foo", std::nullopt}});543 EXPECT_CALL(message_handler, Received(Request{1, "foo", std::nullopt}));544 ASSERT_THAT_ERROR(Run(), Succeeded());545}546 547TEST_F(JSONRPCTransportTest, ReadMultipleMessagesInSingleWrite) {548 InSequence seq;549 Write(Message{Request{1, "one", std::nullopt}},550 Message{test_protocol::Event{"two", std::nullopt}},551 Message{Response{3, 0, "three"}});552 EXPECT_CALL(message_handler, Received(Request{1, "one", std::nullopt}));553 EXPECT_CALL(message_handler,554 Received(test_protocol::Event{"two", std::nullopt}));555 EXPECT_CALL(message_handler, Received(Response{3, 0, "three"}));556 ASSERT_THAT_ERROR(Run(), Succeeded());557}558 559TEST_F(JSONRPCTransportTest, ReadAcrossMultipleChunks) {560 // Use a string longer than the chunk size to ensure we split the message561 // across the chunk boundary.562 std::string long_str = std::string(563 IOTransport<test_protocol::ProtoDesc>::kReadBufferSize * 2, 'x');564 Write(Request{42, long_str, std::nullopt});565 EXPECT_CALL(message_handler, Received(Request{42, long_str, std::nullopt}));566 ASSERT_THAT_ERROR(Run(), Succeeded());567}568 569TEST_F(JSONRPCTransportTest, ReadPartialMessage) {570 std::string message = R"({"id":42,"name":"foo","params":null})"571 "\n";572 std::string part1 = message.substr(0, 7);573 std::string part2 = message.substr(7);574 575 EXPECT_CALL(message_handler, Received(Request{42, "foo", std::nullopt}));576 577 ASSERT_THAT_EXPECTED(input.Write(part1.data(), part1.size()), Succeeded());578 bool addition_succeeded = loop.AddPendingCallback(579 [](MainLoopBase &loop) { loop.RequestTermination(); });580 EXPECT_TRUE(addition_succeeded);581 ASSERT_THAT_ERROR(Run(/*close_input=*/false), Succeeded());582 583 ASSERT_THAT_EXPECTED(input.Write(part2.data(), part2.size()), Succeeded());584 input.CloseWriteFileDescriptor();585 ASSERT_THAT_ERROR(Run(), Succeeded());586}587 588TEST_F(JSONRPCTransportTest, ReadWithEOF) {589 ASSERT_THAT_ERROR(Run(), Succeeded());590}591 592TEST_F(JSONRPCTransportTest, ReaderWithUnhandledData) {593 std::string message = R"json({"req": "foo")json";594 // Write an incomplete message and close the handle.595 ASSERT_THAT_EXPECTED(input.Write(message.data(), message.size() - 1),596 Succeeded());597 598 EXPECT_CALL(message_handler, OnError(_)).WillOnce([](llvm::Error err) {599 ASSERT_THAT_ERROR(std::move(err),600 Failed<TransportUnhandledContentsError>());601 });602 ASSERT_THAT_ERROR(Run(), Succeeded());603}604 605TEST_F(JSONRPCTransportTest, Write) {606 ASSERT_THAT_ERROR(transport->Send(Request{11, "foo", std::nullopt}),607 Succeeded());608 ASSERT_THAT_ERROR(transport->Send(Response{14, 0, "bar"}), Succeeded());609 ASSERT_THAT_ERROR(transport->Send(test_protocol::Event{"baz", std::nullopt}),610 Succeeded());611 output.CloseWriteFileDescriptor();612 char buf[1024];613 Expected<size_t> bytes_read =614 output.Read(buf, sizeof(buf), std::chrono::milliseconds(1));615 ASSERT_THAT_EXPECTED(bytes_read, Succeeded());616 ASSERT_EQ(StringRef(buf, *bytes_read),617 StringRef(R"({"id":11,"name":"foo","params":null})"618 "\n"619 R"({"errorCode":0,"id":14,"result":"bar"})"620 "\n"621 R"({"name":"baz","params":null})"622 "\n"));623}624 625TEST_F(JSONRPCTransportTest, InvalidTransport) {626 transport = std::make_unique<TestJSONRPCTransport>(nullptr, nullptr);627 ASSERT_THAT_ERROR(Run(/*close_input=*/false),628 FailedWithMessage("IO object is not valid."));629}630 631// Out-bound binding request handler.632TEST_F(TransportBinderTest, OutBoundRequests) {633 OutgoingRequest<MyFnResult, MyFnParams> addFn =634 binder->Bind<MyFnResult, MyFnParams>("add");635 bool replied = false;636 addFn(MyFnParams{1, 2}, [&](Expected<MyFnResult> result) {637 EXPECT_THAT_EXPECTED(result, Succeeded());638 EXPECT_EQ(result->c, 3);639 replied = true;640 });641 EXPECT_CALL(remote, Received(Request{1, "add", MyFnParams{1, 2}}));642 EXPECT_THAT_ERROR(from_remote->Send(Response{1, 0, toJSON(MyFnResult{3})}),643 Succeeded());644 Run();645 EXPECT_TRUE(replied);646}647 648TEST_F(TransportBinderTest, OutBoundRequestsVoidParams) {649 OutgoingRequest<MyFnResult, void> voidParamFn =650 binder->Bind<MyFnResult, void>("voidParam");651 bool replied = false;652 voidParamFn([&](Expected<MyFnResult> result) {653 EXPECT_THAT_EXPECTED(result, Succeeded());654 EXPECT_EQ(result->c, 3);655 replied = true;656 });657 EXPECT_CALL(remote, Received(Request{1, "voidParam", std::nullopt}));658 EXPECT_THAT_ERROR(from_remote->Send(Response{1, 0, toJSON(MyFnResult{3})}),659 Succeeded());660 Run();661 EXPECT_TRUE(replied);662}663 664TEST_F(TransportBinderTest, OutBoundRequestsVoidResult) {665 OutgoingRequest<void, MyFnParams> voidResultFn =666 binder->Bind<void, MyFnParams>("voidResult");667 bool replied = false;668 voidResultFn(MyFnParams{4, 5}, [&](llvm::Error error) {669 EXPECT_THAT_ERROR(std::move(error), Succeeded());670 replied = true;671 });672 EXPECT_CALL(remote, Received(Request{1, "voidResult", MyFnParams{4, 5}}));673 EXPECT_THAT_ERROR(from_remote->Send(Response{1, 0, std::nullopt}),674 Succeeded());675 Run();676 EXPECT_TRUE(replied);677}678 679TEST_F(TransportBinderTest, OutBoundRequestsVoidParamsAndVoidResult) {680 OutgoingRequest<void, void> voidParamAndResultFn =681 binder->Bind<void, void>("voidParamAndResult");682 bool replied = false;683 voidParamAndResultFn([&](llvm::Error error) {684 EXPECT_THAT_ERROR(std::move(error), Succeeded());685 replied = true;686 });687 EXPECT_CALL(remote, Received(Request{1, "voidParamAndResult", std::nullopt}));688 EXPECT_THAT_ERROR(from_remote->Send(Response{1, 0, std::nullopt}),689 Succeeded());690 Run();691 EXPECT_TRUE(replied);692}693 694// In-bound binding request handler.695TEST_F(TransportBinderTest, InBoundRequests) {696 bool called = false;697 binder->Bind<MyFnResult, MyFnParams>(698 "add",699 [&](const int captured_param,700 const MyFnParams ¶ms) -> Expected<MyFnResult> {701 called = true;702 return MyFnResult{params.a + params.b + captured_param};703 },704 2);705 EXPECT_THAT_ERROR(from_remote->Send(Request{1, "add", MyFnParams{3, 4}}),706 Succeeded());707 708 EXPECT_CALL(remote, Received(Response{1, 0, MyFnResult{9}}));709 Run();710 EXPECT_TRUE(called);711}712 713TEST_F(TransportBinderTest, InBoundRequestsVoidParams) {714 bool called = false;715 binder->Bind<MyFnResult, void>(716 "voidParam",717 [&](const int captured_param) -> Expected<MyFnResult> {718 called = true;719 return MyFnResult{captured_param};720 },721 2);722 EXPECT_THAT_ERROR(from_remote->Send(Request{2, "voidParam", std::nullopt}),723 Succeeded());724 EXPECT_CALL(remote, Received(Response{2, 0, MyFnResult{2}}));725 Run();726 EXPECT_TRUE(called);727}728 729TEST_F(TransportBinderTest, InBoundRequestsVoidResult) {730 bool called = false;731 binder->Bind<void, MyFnParams>(732 "voidResult",733 [&](const int captured_param, const MyFnParams ¶ms) -> llvm::Error {734 called = true;735 EXPECT_EQ(captured_param, 2);736 EXPECT_EQ(params.a, 3);737 EXPECT_EQ(params.b, 4);738 return llvm::Error::success();739 },740 2);741 EXPECT_THAT_ERROR(742 from_remote->Send(Request{3, "voidResult", MyFnParams{3, 4}}),743 Succeeded());744 EXPECT_CALL(remote, Received(Response{3, 0, std::nullopt}));745 Run();746 EXPECT_TRUE(called);747}748TEST_F(TransportBinderTest, InBoundRequestsVoidParamsAndResult) {749 bool called = false;750 binder->Bind<void, void>(751 "voidParamAndResult",752 [&](const int captured_param) -> llvm::Error {753 called = true;754 EXPECT_EQ(captured_param, 2);755 return llvm::Error::success();756 },757 2);758 EXPECT_THAT_ERROR(759 from_remote->Send(Request{4, "voidParamAndResult", std::nullopt}),760 Succeeded());761 EXPECT_CALL(remote, Received(Response{4, 0, std::nullopt}));762 Run();763 EXPECT_TRUE(called);764}765 766// Out-bound binding event handler.767TEST_F(TransportBinderTest, OutBoundEvents) {768 OutgoingEvent<MyFnParams> emitEvent = binder->Bind<MyFnParams>("evt");769 emitEvent(MyFnParams{1, 2});770 EXPECT_CALL(remote, Received(test_protocol::Event{"evt", MyFnParams{1, 2}}));771 Run();772}773 774TEST_F(TransportBinderTest, OutBoundEventsVoidParams) {775 OutgoingEvent<void> emitEvent = binder->Bind<void>("evt");776 emitEvent();777 EXPECT_CALL(remote, Received(test_protocol::Event{"evt", std::nullopt}));778 Run();779}780 781// In-bound binding event handler.782TEST_F(TransportBinderTest, InBoundEvents) {783 bool called = false;784 binder->Bind<MyFnParams>(785 "evt",786 [&](const int captured_arg, const MyFnParams ¶ms) {787 EXPECT_EQ(captured_arg, 42);788 EXPECT_EQ(params.a, 3);789 EXPECT_EQ(params.b, 4);790 called = true;791 },792 42);793 EXPECT_THAT_ERROR(794 from_remote->Send(test_protocol::Event{"evt", MyFnParams{3, 4}}),795 Succeeded());796 Run();797 EXPECT_TRUE(called);798}799 800TEST_F(TransportBinderTest, InBoundEventsVoidParams) {801 bool called = false;802 binder->Bind<void>(803 "evt",804 [&](const int captured_arg) {805 EXPECT_EQ(captured_arg, 42);806 called = true;807 },808 42);809 EXPECT_THAT_ERROR(810 from_remote->Send(test_protocol::Event{"evt", std::nullopt}),811 Succeeded());812 Run();813 EXPECT_TRUE(called);814}815 816#endif817