895 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			895 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===----------- RPCUtilsTest.cpp - Unit tests the Orc RPC utils ----------===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "llvm/ExecutionEngine/Orc/RPC/RPCUtils.h"
 | |
| #include "QueueChannel.h"
 | |
| #include "gtest/gtest.h"
 | |
| 
 | |
| #include <queue>
 | |
| 
 | |
| using namespace llvm;
 | |
| using namespace llvm::orc;
 | |
| using namespace llvm::orc::rpc;
 | |
| 
 | |
| class RPCFoo {};
 | |
| 
 | |
| namespace llvm {
 | |
| namespace orc {
 | |
| namespace rpc {
 | |
| 
 | |
|   template <>
 | |
|   class RPCTypeName<RPCFoo> {
 | |
|   public:
 | |
|     static const char* getName() { return "RPCFoo"; }
 | |
|   };
 | |
| 
 | |
|   template <>
 | |
|   class SerializationTraits<QueueChannel, RPCFoo, RPCFoo> {
 | |
|   public:
 | |
|     static Error serialize(QueueChannel&, const RPCFoo&) {
 | |
|       return Error::success();
 | |
|     }
 | |
| 
 | |
|     static Error deserialize(QueueChannel&, RPCFoo&) {
 | |
|       return Error::success();
 | |
|     }
 | |
|   };
 | |
| 
 | |
| } // end namespace rpc
 | |
| } // end namespace orc
 | |
| } // end namespace llvm
 | |
| 
 | |
| class RPCBar {};
 | |
| 
 | |
| class DummyError : public ErrorInfo<DummyError> {
 | |
| public:
 | |
| 
 | |
|   static char ID;
 | |
| 
 | |
|   DummyError(uint32_t Val) : Val(Val) {}
 | |
| 
 | |
|   std::error_code convertToErrorCode() const override {
 | |
|     // Use a nonsense error code - we want to verify that errors
 | |
|     // transmitted over the network are replaced with
 | |
|     // OrcErrorCode::UnknownErrorCodeFromRemote.
 | |
|     return orcError(OrcErrorCode::RemoteAllocatorDoesNotExist);
 | |
|   }
 | |
| 
 | |
|   void log(raw_ostream &OS) const override {
 | |
|     OS << "Dummy error " << Val;
 | |
|   }
 | |
| 
 | |
|   uint32_t getValue() const { return Val; }
 | |
| 
 | |
| public:
 | |
|   uint32_t Val;
 | |
| };
 | |
| 
 | |
| char DummyError::ID = 0;
 | |
| 
 | |
| template <typename ChannelT>
 | |
| void registerDummyErrorSerialization() {
 | |
|   static bool AlreadyRegistered = false;
 | |
|   if (!AlreadyRegistered) {
 | |
|     SerializationTraits<ChannelT, Error>::
 | |
|       template registerErrorType<DummyError>(
 | |
|         "DummyError",
 | |
|         [](ChannelT &C, const DummyError &DE) {
 | |
|           return serializeSeq(C, DE.getValue());
 | |
|         },
 | |
|         [](ChannelT &C, Error &Err) -> Error {
 | |
|           ErrorAsOutParameter EAO(&Err);
 | |
|           uint32_t Val;
 | |
|           if (auto Err = deserializeSeq(C, Val))
 | |
|             return Err;
 | |
|           Err = make_error<DummyError>(Val);
 | |
|           return Error::success();
 | |
|         });
 | |
|     AlreadyRegistered = true;
 | |
|   }
 | |
| }
 | |
| 
 | |
| namespace llvm {
 | |
| namespace orc {
 | |
| namespace rpc {
 | |
| 
 | |
|   template <>
 | |
|   class SerializationTraits<QueueChannel, RPCFoo, RPCBar> {
 | |
|   public:
 | |
|     static Error serialize(QueueChannel&, const RPCBar&) {
 | |
|       return Error::success();
 | |
|     }
 | |
| 
 | |
|     static Error deserialize(QueueChannel&, RPCBar&) {
 | |
|       return Error::success();
 | |
|     }
 | |
| };
 | |
| 
 | |
| } // end namespace rpc
 | |
| } // end namespace orc
 | |
| } // end namespace llvm
 | |
| 
 | |
| namespace DummyRPCAPI {
 | |
| 
 | |
|   class VoidBool : public Function<VoidBool, void(bool)> {
 | |
|   public:
 | |
|     static const char* getName() { return "VoidBool"; }
 | |
|   };
 | |
| 
 | |
|   class IntInt : public Function<IntInt, int32_t(int32_t)> {
 | |
|   public:
 | |
|     static const char* getName() { return "IntInt"; }
 | |
|   };
 | |
| 
 | |
|   class VoidString : public Function<VoidString, void(std::string)> {
 | |
|   public:
 | |
|     static const char* getName() { return "VoidString"; }
 | |
|   };
 | |
| 
 | |
|   class AllTheTypes
 | |
|       : public Function<AllTheTypes, void(int8_t, uint8_t, int16_t, uint16_t,
 | |
|                                           int32_t, uint32_t, int64_t, uint64_t,
 | |
|                                           bool, std::string, std::vector<int>,
 | |
|                                           std::set<int>, std::map<int, bool>)> {
 | |
|   public:
 | |
|     static const char* getName() { return "AllTheTypes"; }
 | |
|   };
 | |
| 
 | |
|   class CustomType : public Function<CustomType, RPCFoo(RPCFoo)> {
 | |
|   public:
 | |
|     static const char* getName() { return "CustomType"; }
 | |
|   };
 | |
| 
 | |
|   class ErrorFunc : public Function<ErrorFunc, Error()> {
 | |
|   public:
 | |
|     static const char* getName() { return "ErrorFunc"; }
 | |
|   };
 | |
| 
 | |
|   class ExpectedFunc : public Function<ExpectedFunc, Expected<uint32_t>()> {
 | |
|   public:
 | |
|     static const char* getName() { return "ExpectedFunc"; }
 | |
|   };
 | |
| 
 | |
| }
 | |
| 
 | |
| class DummyRPCEndpoint : public SingleThreadedRPCEndpoint<QueueChannel> {
 | |
| public:
 | |
|   DummyRPCEndpoint(QueueChannel &C)
 | |
|       : SingleThreadedRPCEndpoint(C, true) {}
 | |
| };
 | |
| 
 | |
| 
 | |
| void freeVoidBool(bool B) {
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, TestFreeFunctionHandler) {
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Server(*Channels.first);
 | |
|   Server.addHandler<DummyRPCAPI::VoidBool>(freeVoidBool);
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, TestCallAsyncVoidBool) {
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Client(*Channels.first);
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   std::thread ServerThread([&]() {
 | |
|       Server.addHandler<DummyRPCAPI::VoidBool>(
 | |
|           [](bool B) {
 | |
|             EXPECT_EQ(B, true)
 | |
|               << "Server void(bool) received unexpected result";
 | |
|           });
 | |
| 
 | |
|       {
 | |
|         // Poke the server to handle the negotiate call.
 | |
|         auto Err = Server.handleOne();
 | |
|         EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate";
 | |
|       }
 | |
| 
 | |
|       {
 | |
|         // Poke the server to handle the VoidBool call.
 | |
|         auto Err = Server.handleOne();
 | |
|         EXPECT_FALSE(!!Err) << "Server failed to handle call to void(bool)";
 | |
|       }
 | |
|   });
 | |
| 
 | |
|   {
 | |
|     // Make an async call.
 | |
|     auto Err = Client.callAsync<DummyRPCAPI::VoidBool>(
 | |
|         [](Error Err) {
 | |
|           EXPECT_FALSE(!!Err) << "Async void(bool) response handler failed";
 | |
|           return Error::success();
 | |
|         }, true);
 | |
|     EXPECT_FALSE(!!Err) << "Client.callAsync failed for void(bool)";
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     // Poke the client to process the result of the void(bool) call.
 | |
|     auto Err = Client.handleOne();
 | |
|     EXPECT_FALSE(!!Err) << "Client failed to handle response from void(bool)";
 | |
|   }
 | |
| 
 | |
|   ServerThread.join();
 | |
| 
 | |
|   // The client should have made two calls to send: One implicit call to
 | |
|   // negotiate the VoidBool function key, and a second to make the VoidBool
 | |
|   // call.
 | |
|   EXPECT_EQ(Channels.first->SendCalls, 2U)
 | |
|       << "Expected one send call to have been made by client";
 | |
| 
 | |
|   // The server should have made two calls to send: One to send the response to
 | |
|   // the negotiate call, and another to send the response to the VoidBool call.
 | |
|   EXPECT_EQ(Channels.second->SendCalls, 2U)
 | |
|       << "Expected two send calls to have been made by server";
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, TestCallAsyncIntInt) {
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Client(*Channels.first);
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   std::thread ServerThread([&]() {
 | |
|       Server.addHandler<DummyRPCAPI::IntInt>(
 | |
|           [](int X) -> int {
 | |
|             EXPECT_EQ(X, 21) << "Server int(int) receieved unexpected result";
 | |
|             return 2 * X;
 | |
|           });
 | |
| 
 | |
|       {
 | |
|         // Poke the server to handle the negotiate call.
 | |
|         auto Err = Server.handleOne();
 | |
|         EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate";
 | |
|       }
 | |
| 
 | |
|       {
 | |
|         // Poke the server to handle the int(int) call.
 | |
|         auto Err = Server.handleOne();
 | |
|         EXPECT_FALSE(!!Err) << "Server failed to handle call to int(int)";
 | |
|       }
 | |
|     });
 | |
| 
 | |
|   {
 | |
|     auto Err = Client.callAsync<DummyRPCAPI::IntInt>(
 | |
|         [](Expected<int> Result) {
 | |
|           EXPECT_TRUE(!!Result) << "Async int(int) response handler failed";
 | |
|           EXPECT_EQ(*Result, 42)
 | |
|             << "Async int(int) response handler received incorrect result";
 | |
|           return Error::success();
 | |
|         }, 21);
 | |
|     EXPECT_FALSE(!!Err) << "Client.callAsync failed for int(int)";
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     // Poke the client to process the result.
 | |
|     auto Err = Client.handleOne();
 | |
|     EXPECT_FALSE(!!Err) << "Client failed to handle response from void(bool)";
 | |
|   }
 | |
| 
 | |
|   ServerThread.join();
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, TestAsyncVoidBoolHandler) {
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Client(*Channels.first);
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   std::thread ServerThread([&]() {
 | |
|       Server.addAsyncHandler<DummyRPCAPI::VoidBool>(
 | |
|           [](std::function<Error(Error)> SendResult,
 | |
|              bool B) {
 | |
|             EXPECT_EQ(B, true) << "Server void(bool) receieved unexpected result";
 | |
|             cantFail(SendResult(Error::success()));
 | |
|             return Error::success();
 | |
|           });
 | |
| 
 | |
|       {
 | |
|         // Poke the server to handle the negotiate call.
 | |
|         auto Err = Server.handleOne();
 | |
|         EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate";
 | |
|       }
 | |
| 
 | |
|       {
 | |
|         // Poke the server to handle the VoidBool call.
 | |
|         auto Err = Server.handleOne();
 | |
|         EXPECT_FALSE(!!Err) << "Server failed to handle call to void(bool)";
 | |
|       }
 | |
|   });
 | |
| 
 | |
|   {
 | |
|     auto Err = Client.callAsync<DummyRPCAPI::VoidBool>(
 | |
|         [](Error Result) {
 | |
|           EXPECT_FALSE(!!Result) << "Async void(bool) response handler failed";
 | |
|           return Error::success();
 | |
|         }, true);
 | |
|     EXPECT_FALSE(!!Err) << "Client.callAsync failed for void(bool)";
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     // Poke the client to process the result.
 | |
|     auto Err = Client.handleOne();
 | |
|     EXPECT_FALSE(!!Err) << "Client failed to handle response from void(bool)";
 | |
|   }
 | |
| 
 | |
|   ServerThread.join();
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, TestAsyncIntIntHandler) {
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Client(*Channels.first);
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   std::thread ServerThread([&]() {
 | |
|       Server.addAsyncHandler<DummyRPCAPI::IntInt>(
 | |
|           [](std::function<Error(Expected<int32_t>)> SendResult,
 | |
|              int32_t X) {
 | |
|             EXPECT_EQ(X, 21) << "Server int(int) receieved unexpected result";
 | |
|             return SendResult(2 * X);
 | |
|           });
 | |
| 
 | |
|       {
 | |
|         // Poke the server to handle the negotiate call.
 | |
|         auto Err = Server.handleOne();
 | |
|         EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate";
 | |
|       }
 | |
| 
 | |
|       {
 | |
|         // Poke the server to handle the VoidBool call.
 | |
|         auto Err = Server.handleOne();
 | |
|         EXPECT_FALSE(!!Err) << "Server failed to handle call to void(bool)";
 | |
|       }
 | |
|   });
 | |
| 
 | |
|   {
 | |
|     auto Err = Client.callAsync<DummyRPCAPI::IntInt>(
 | |
|         [](Expected<int> Result) {
 | |
|           EXPECT_TRUE(!!Result) << "Async int(int) response handler failed";
 | |
|           EXPECT_EQ(*Result, 42)
 | |
|             << "Async int(int) response handler received incorrect result";
 | |
|           return Error::success();
 | |
|         }, 21);
 | |
|     EXPECT_FALSE(!!Err) << "Client.callAsync failed for int(int)";
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     // Poke the client to process the result.
 | |
|     auto Err = Client.handleOne();
 | |
|     EXPECT_FALSE(!!Err) << "Client failed to handle response from void(bool)";
 | |
|   }
 | |
| 
 | |
|   ServerThread.join();
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, TestAsyncIntIntHandlerMethod) {
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Client(*Channels.first);
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   class Dummy {
 | |
|   public:
 | |
|     Error handler(std::function<Error(Expected<int32_t>)> SendResult,
 | |
|              int32_t X) {
 | |
|       EXPECT_EQ(X, 21) << "Server int(int) receieved unexpected result";
 | |
|       return SendResult(2 * X);
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   std::thread ServerThread([&]() {
 | |
|       Dummy D;
 | |
|       Server.addAsyncHandler<DummyRPCAPI::IntInt>(D, &Dummy::handler);
 | |
| 
 | |
|       {
 | |
|         // Poke the server to handle the negotiate call.
 | |
|         auto Err = Server.handleOne();
 | |
|         EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate";
 | |
|       }
 | |
| 
 | |
|       {
 | |
|         // Poke the server to handle the VoidBool call.
 | |
|         auto Err = Server.handleOne();
 | |
|         EXPECT_FALSE(!!Err) << "Server failed to handle call to void(bool)";
 | |
|       }
 | |
|   });
 | |
| 
 | |
|   {
 | |
|     auto Err = Client.callAsync<DummyRPCAPI::IntInt>(
 | |
|         [](Expected<int> Result) {
 | |
|           EXPECT_TRUE(!!Result) << "Async int(int) response handler failed";
 | |
|           EXPECT_EQ(*Result, 42)
 | |
|             << "Async int(int) response handler received incorrect result";
 | |
|           return Error::success();
 | |
|         }, 21);
 | |
|     EXPECT_FALSE(!!Err) << "Client.callAsync failed for int(int)";
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     // Poke the client to process the result.
 | |
|     auto Err = Client.handleOne();
 | |
|     EXPECT_FALSE(!!Err) << "Client failed to handle response from void(bool)";
 | |
|   }
 | |
| 
 | |
|   ServerThread.join();
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, TestCallAsyncVoidString) {
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Client(*Channels.first);
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   std::thread ServerThread([&]() {
 | |
|       Server.addHandler<DummyRPCAPI::VoidString>(
 | |
|           [](const std::string &S) {
 | |
|             EXPECT_EQ(S, "hello")
 | |
|               << "Server void(std::string) received unexpected result";
 | |
|           });
 | |
| 
 | |
|       // Poke the server to handle the negotiate call.
 | |
|       for (int I = 0; I < 4; ++I) {
 | |
|         auto Err = Server.handleOne();
 | |
|         EXPECT_FALSE(!!Err) << "Server failed to handle call";
 | |
|       }
 | |
|   });
 | |
| 
 | |
|   {
 | |
|     // Make an call using a std::string.
 | |
|     auto Err = Client.callB<DummyRPCAPI::VoidString>(std::string("hello"));
 | |
|     EXPECT_FALSE(!!Err) << "Client.callAsync failed for void(std::string)";
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     // Make an call using a std::string.
 | |
|     auto Err = Client.callB<DummyRPCAPI::VoidString>(StringRef("hello"));
 | |
|     EXPECT_FALSE(!!Err) << "Client.callAsync failed for void(std::string)";
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     // Make an call using a std::string.
 | |
|     auto Err = Client.callB<DummyRPCAPI::VoidString>("hello");
 | |
|     EXPECT_FALSE(!!Err) << "Client.callAsync failed for void(string)";
 | |
|   }
 | |
| 
 | |
|   ServerThread.join();
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, TestSerialization) {
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Client(*Channels.first);
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   std::thread ServerThread([&]() {
 | |
|     Server.addHandler<DummyRPCAPI::AllTheTypes>([&](int8_t S8, uint8_t U8,
 | |
|                                                     int16_t S16, uint16_t U16,
 | |
|                                                     int32_t S32, uint32_t U32,
 | |
|                                                     int64_t S64, uint64_t U64,
 | |
|                                                     bool B, std::string S,
 | |
|                                                     std::vector<int> V,
 | |
|                                                     std::set<int> S2,
 | |
|                                                     std::map<int, bool> M) {
 | |
|       EXPECT_EQ(S8, -101) << "int8_t serialization broken";
 | |
|       EXPECT_EQ(U8, 250) << "uint8_t serialization broken";
 | |
|       EXPECT_EQ(S16, -10000) << "int16_t serialization broken";
 | |
|       EXPECT_EQ(U16, 10000) << "uint16_t serialization broken";
 | |
|       EXPECT_EQ(S32, -1000000000) << "int32_t serialization broken";
 | |
|       EXPECT_EQ(U32, 1000000000ULL) << "uint32_t serialization broken";
 | |
|       EXPECT_EQ(S64, -10000000000) << "int64_t serialization broken";
 | |
|       EXPECT_EQ(U64, 10000000000ULL) << "uint64_t serialization broken";
 | |
|       EXPECT_EQ(B, true) << "bool serialization broken";
 | |
|       EXPECT_EQ(S, "foo") << "std::string serialization broken";
 | |
|       EXPECT_EQ(V, std::vector<int>({42, 7}))
 | |
|           << "std::vector serialization broken";
 | |
|       EXPECT_EQ(S2, std::set<int>({7, 42})) << "std::set serialization broken";
 | |
|       EXPECT_EQ(M, (std::map<int, bool>({{7, false}, {42, true}})))
 | |
|           << "std::map serialization broken";
 | |
|       return Error::success();
 | |
|     });
 | |
| 
 | |
|     {
 | |
|       // Poke the server to handle the negotiate call.
 | |
|       auto Err = Server.handleOne();
 | |
|       EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate";
 | |
|     }
 | |
| 
 | |
|     {
 | |
|       // Poke the server to handle the AllTheTypes call.
 | |
|       auto Err = Server.handleOne();
 | |
|       EXPECT_FALSE(!!Err) << "Server failed to handle call to void(bool)";
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   {
 | |
|     // Make an async call.
 | |
|     std::vector<int> V({42, 7});
 | |
|     std::set<int> S({7, 42});
 | |
|     std::map<int, bool> M({{7, false}, {42, true}});
 | |
|     auto Err = Client.callAsync<DummyRPCAPI::AllTheTypes>(
 | |
|         [](Error Err) {
 | |
|           EXPECT_FALSE(!!Err) << "Async AllTheTypes response handler failed";
 | |
|           return Error::success();
 | |
|         },
 | |
|         static_cast<int8_t>(-101), static_cast<uint8_t>(250),
 | |
|         static_cast<int16_t>(-10000), static_cast<uint16_t>(10000),
 | |
|         static_cast<int32_t>(-1000000000), static_cast<uint32_t>(1000000000),
 | |
|         static_cast<int64_t>(-10000000000), static_cast<uint64_t>(10000000000),
 | |
|         true, std::string("foo"), V, S, M);
 | |
|     EXPECT_FALSE(!!Err) << "Client.callAsync failed for AllTheTypes";
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     // Poke the client to process the result of the AllTheTypes call.
 | |
|     auto Err = Client.handleOne();
 | |
|     EXPECT_FALSE(!!Err) << "Client failed to handle response from AllTheTypes";
 | |
|   }
 | |
| 
 | |
|   ServerThread.join();
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, TestCustomType) {
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Client(*Channels.first);
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   std::thread ServerThread([&]() {
 | |
|       Server.addHandler<DummyRPCAPI::CustomType>(
 | |
|           [](RPCFoo F) {});
 | |
| 
 | |
|       {
 | |
|         // Poke the server to handle the negotiate call.
 | |
|         auto Err = Server.handleOne();
 | |
|         EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate";
 | |
|       }
 | |
| 
 | |
|       {
 | |
|         // Poke the server to handle the CustomType call.
 | |
|         auto Err = Server.handleOne();
 | |
|         EXPECT_FALSE(!!Err) << "Server failed to handle call to RPCFoo(RPCFoo)";
 | |
|       }
 | |
|   });
 | |
| 
 | |
|   {
 | |
|     // Make an async call.
 | |
|     auto Err = Client.callAsync<DummyRPCAPI::CustomType>(
 | |
|         [](Expected<RPCFoo> FOrErr) {
 | |
|           EXPECT_TRUE(!!FOrErr)
 | |
|             << "Async RPCFoo(RPCFoo) response handler failed";
 | |
|           return Error::success();
 | |
|         }, RPCFoo());
 | |
|     EXPECT_FALSE(!!Err) << "Client.callAsync failed for RPCFoo(RPCFoo)";
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     // Poke the client to process the result of the RPCFoo() call.
 | |
|     auto Err = Client.handleOne();
 | |
|     EXPECT_FALSE(!!Err)
 | |
|       << "Client failed to handle response from RPCFoo(RPCFoo)";
 | |
|   }
 | |
| 
 | |
|   ServerThread.join();
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, TestWithAltCustomType) {
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Client(*Channels.first);
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   std::thread ServerThread([&]() {
 | |
|       Server.addHandler<DummyRPCAPI::CustomType>(
 | |
|           [](RPCBar F) {});
 | |
| 
 | |
|       {
 | |
|         // Poke the server to handle the negotiate call.
 | |
|         auto Err = Server.handleOne();
 | |
|         EXPECT_FALSE(!!Err) << "Server failed to handle call to negotiate";
 | |
|       }
 | |
| 
 | |
|       {
 | |
|         // Poke the server to handle the CustomType call.
 | |
|         auto Err = Server.handleOne();
 | |
|         EXPECT_FALSE(!!Err) << "Server failed to handle call to RPCFoo(RPCFoo)";
 | |
|       }
 | |
|   });
 | |
| 
 | |
|   {
 | |
|     // Make an async call.
 | |
|     auto Err = Client.callAsync<DummyRPCAPI::CustomType>(
 | |
|         [](Expected<RPCBar> FOrErr) {
 | |
|           EXPECT_TRUE(!!FOrErr)
 | |
|             << "Async RPCFoo(RPCFoo) response handler failed";
 | |
|           return Error::success();
 | |
|         }, RPCBar());
 | |
|     EXPECT_FALSE(!!Err) << "Client.callAsync failed for RPCFoo(RPCFoo)";
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     // Poke the client to process the result of the RPCFoo() call.
 | |
|     auto Err = Client.handleOne();
 | |
|     EXPECT_FALSE(!!Err)
 | |
|       << "Client failed to handle response from RPCFoo(RPCFoo)";
 | |
|   }
 | |
| 
 | |
|   ServerThread.join();
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, ReturnErrorSuccess) {
 | |
|   registerDummyErrorSerialization<QueueChannel>();
 | |
| 
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Client(*Channels.first);
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   std::thread ServerThread([&]() {
 | |
|       Server.addHandler<DummyRPCAPI::ErrorFunc>(
 | |
|         []() {
 | |
|           return Error::success();
 | |
|         });
 | |
| 
 | |
|       // Handle the negotiate plus one call.
 | |
|       for (unsigned I = 0; I != 2; ++I)
 | |
|         cantFail(Server.handleOne());
 | |
|     });
 | |
| 
 | |
|   cantFail(Client.callAsync<DummyRPCAPI::ErrorFunc>(
 | |
|              [&](Error Err) {
 | |
|                EXPECT_FALSE(!!Err) << "Expected success value";
 | |
|                return Error::success();
 | |
|              }));
 | |
| 
 | |
|   cantFail(Client.handleOne());
 | |
| 
 | |
|   ServerThread.join();
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, ReturnErrorFailure) {
 | |
|   registerDummyErrorSerialization<QueueChannel>();
 | |
| 
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Client(*Channels.first);
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   std::thread ServerThread([&]() {
 | |
|       Server.addHandler<DummyRPCAPI::ErrorFunc>(
 | |
|         []() {
 | |
|           return make_error<DummyError>(42);
 | |
|         });
 | |
| 
 | |
|       // Handle the negotiate plus one call.
 | |
|       for (unsigned I = 0; I != 2; ++I)
 | |
|         cantFail(Server.handleOne());
 | |
|     });
 | |
| 
 | |
|   cantFail(Client.callAsync<DummyRPCAPI::ErrorFunc>(
 | |
|              [&](Error Err) {
 | |
|                EXPECT_TRUE(Err.isA<DummyError>())
 | |
|                  << "Incorrect error type";
 | |
|                return handleErrors(
 | |
|                         std::move(Err),
 | |
|                         [](const DummyError &DE) {
 | |
|                           EXPECT_EQ(DE.getValue(), 42ULL)
 | |
|                             << "Incorrect DummyError serialization";
 | |
|                         });
 | |
|              }));
 | |
| 
 | |
|   cantFail(Client.handleOne());
 | |
| 
 | |
|   ServerThread.join();
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, ReturnExpectedSuccess) {
 | |
|   registerDummyErrorSerialization<QueueChannel>();
 | |
| 
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Client(*Channels.first);
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   std::thread ServerThread([&]() {
 | |
|       Server.addHandler<DummyRPCAPI::ExpectedFunc>(
 | |
|         []() -> uint32_t {
 | |
|           return 42;
 | |
|         });
 | |
| 
 | |
|       // Handle the negotiate plus one call.
 | |
|       for (unsigned I = 0; I != 2; ++I)
 | |
|         cantFail(Server.handleOne());
 | |
|     });
 | |
| 
 | |
|   cantFail(Client.callAsync<DummyRPCAPI::ExpectedFunc>(
 | |
|                [&](Expected<uint32_t> ValOrErr) {
 | |
|                  EXPECT_TRUE(!!ValOrErr)
 | |
|                    << "Expected success value";
 | |
|                  EXPECT_EQ(*ValOrErr, 42ULL)
 | |
|                    << "Incorrect Expected<uint32_t> deserialization";
 | |
|                  return Error::success();
 | |
|                }));
 | |
| 
 | |
|   cantFail(Client.handleOne());
 | |
| 
 | |
|   ServerThread.join();
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, ReturnExpectedFailure) {
 | |
|   registerDummyErrorSerialization<QueueChannel>();
 | |
| 
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Client(*Channels.first);
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   std::thread ServerThread([&]() {
 | |
|       Server.addHandler<DummyRPCAPI::ExpectedFunc>(
 | |
|         []() -> Expected<uint32_t> {
 | |
|           return make_error<DummyError>(7);
 | |
|         });
 | |
| 
 | |
|       // Handle the negotiate plus one call.
 | |
|       for (unsigned I = 0; I != 2; ++I)
 | |
|         cantFail(Server.handleOne());
 | |
|     });
 | |
| 
 | |
|   cantFail(Client.callAsync<DummyRPCAPI::ExpectedFunc>(
 | |
|                [&](Expected<uint32_t> ValOrErr) {
 | |
|                  EXPECT_FALSE(!!ValOrErr)
 | |
|                    << "Expected failure value";
 | |
|                  auto Err = ValOrErr.takeError();
 | |
|                  EXPECT_TRUE(Err.isA<DummyError>())
 | |
|                    << "Incorrect error type";
 | |
|                  return handleErrors(
 | |
|                           std::move(Err),
 | |
|                           [](const DummyError &DE) {
 | |
|                             EXPECT_EQ(DE.getValue(), 7ULL)
 | |
|                               << "Incorrect DummyError serialization";
 | |
|                           });
 | |
|                }));
 | |
| 
 | |
|   cantFail(Client.handleOne());
 | |
| 
 | |
|   ServerThread.join();
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, TestParallelCallGroup) {
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Client(*Channels.first);
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   std::thread ServerThread([&]() {
 | |
|       Server.addHandler<DummyRPCAPI::IntInt>(
 | |
|           [](int X) -> int {
 | |
|             return 2 * X;
 | |
|           });
 | |
| 
 | |
|       // Handle the negotiate, plus three calls.
 | |
|       for (unsigned I = 0; I != 4; ++I) {
 | |
|         auto Err = Server.handleOne();
 | |
|         EXPECT_FALSE(!!Err) << "Server failed to handle call to int(int)";
 | |
|       }
 | |
|     });
 | |
| 
 | |
|   {
 | |
|     int A, B, C;
 | |
|     ParallelCallGroup PCG;
 | |
| 
 | |
|     {
 | |
|       auto Err = PCG.call(
 | |
|         rpcAsyncDispatch<DummyRPCAPI::IntInt>(Client),
 | |
|         [&A](Expected<int> Result) {
 | |
|           EXPECT_TRUE(!!Result) << "Async int(int) response handler failed";
 | |
|           A = *Result;
 | |
|           return Error::success();
 | |
|         }, 1);
 | |
|       EXPECT_FALSE(!!Err) << "First parallel call failed for int(int)";
 | |
|     }
 | |
| 
 | |
|     {
 | |
|       auto Err = PCG.call(
 | |
|         rpcAsyncDispatch<DummyRPCAPI::IntInt>(Client),
 | |
|         [&B](Expected<int> Result) {
 | |
|           EXPECT_TRUE(!!Result) << "Async int(int) response handler failed";
 | |
|           B = *Result;
 | |
|           return Error::success();
 | |
|         }, 2);
 | |
|       EXPECT_FALSE(!!Err) << "Second parallel call failed for int(int)";
 | |
|     }
 | |
| 
 | |
|     {
 | |
|       auto Err = PCG.call(
 | |
|         rpcAsyncDispatch<DummyRPCAPI::IntInt>(Client),
 | |
|         [&C](Expected<int> Result) {
 | |
|           EXPECT_TRUE(!!Result) << "Async int(int) response handler failed";
 | |
|           C = *Result;
 | |
|           return Error::success();
 | |
|         }, 3);
 | |
|       EXPECT_FALSE(!!Err) << "Third parallel call failed for int(int)";
 | |
|     }
 | |
| 
 | |
|     // Handle the three int(int) results.
 | |
|     for (unsigned I = 0; I != 3; ++I) {
 | |
|       auto Err = Client.handleOne();
 | |
|       EXPECT_FALSE(!!Err) << "Client failed to handle response from void(bool)";
 | |
|     }
 | |
| 
 | |
|     PCG.wait();
 | |
| 
 | |
|     EXPECT_EQ(A, 2) << "First parallel call returned bogus result";
 | |
|     EXPECT_EQ(B, 4) << "Second parallel call returned bogus result";
 | |
|     EXPECT_EQ(C, 6) << "Third parallel call returned bogus result";
 | |
|   }
 | |
| 
 | |
|   ServerThread.join();
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, TestAPICalls) {
 | |
| 
 | |
|   using DummyCalls1 = APICalls<DummyRPCAPI::VoidBool, DummyRPCAPI::IntInt>;
 | |
|   using DummyCalls2 = APICalls<DummyRPCAPI::AllTheTypes>;
 | |
|   using DummyCalls3 = APICalls<DummyCalls1, DummyRPCAPI::CustomType>;
 | |
|   using DummyCallsAll = APICalls<DummyCalls1, DummyCalls2, DummyRPCAPI::CustomType>;
 | |
| 
 | |
|   static_assert(DummyCalls1::Contains<DummyRPCAPI::VoidBool>::value,
 | |
|                 "Contains<Func> template should return true here");
 | |
|   static_assert(!DummyCalls1::Contains<DummyRPCAPI::CustomType>::value,
 | |
|                 "Contains<Func> template should return false here");
 | |
| 
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Client(*Channels.first);
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   std::thread ServerThread(
 | |
|     [&]() {
 | |
|       Server.addHandler<DummyRPCAPI::VoidBool>([](bool b) { });
 | |
|       Server.addHandler<DummyRPCAPI::IntInt>([](int x) { return x; });
 | |
|       Server.addHandler<DummyRPCAPI::CustomType>([](RPCFoo F) {});
 | |
| 
 | |
|       for (unsigned I = 0; I < 4; ++I) {
 | |
|         auto Err = Server.handleOne();
 | |
|         (void)!!Err;
 | |
|       }
 | |
|     });
 | |
| 
 | |
|   {
 | |
|     auto Err = DummyCalls1::negotiate(Client);
 | |
|     EXPECT_FALSE(!!Err) << "DummyCalls1::negotiate failed";
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     auto Err = DummyCalls3::negotiate(Client);
 | |
|     EXPECT_FALSE(!!Err) << "DummyCalls3::negotiate failed";
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     auto Err = DummyCallsAll::negotiate(Client);
 | |
|     EXPECT_TRUE(Err.isA<CouldNotNegotiate>())
 | |
|       << "Expected CouldNotNegotiate error for attempted negotiate of "
 | |
|          "unsupported function";
 | |
|     consumeError(std::move(Err));
 | |
|   }
 | |
| 
 | |
|   ServerThread.join();
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, TestRemoveHandler) {
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   Server.addHandler<DummyRPCAPI::VoidBool>(
 | |
|     [](bool B) {
 | |
|       EXPECT_EQ(B, true)
 | |
|         << "Server void(bool) received unexpected result";
 | |
|     });
 | |
| 
 | |
|   Server.removeHandler<DummyRPCAPI::VoidBool>();
 | |
| }
 | |
| 
 | |
| TEST(DummyRPC, TestClearHandlers) {
 | |
|   auto Channels = createPairedQueueChannels();
 | |
|   DummyRPCEndpoint Server(*Channels.second);
 | |
| 
 | |
|   Server.addHandler<DummyRPCAPI::VoidBool>(
 | |
|     [](bool B) {
 | |
|       EXPECT_EQ(B, true)
 | |
|         << "Server void(bool) received unexpected result";
 | |
|     });
 | |
| 
 | |
|   Server.clearHandlers();
 | |
| }
 |