//===----------- RPCUtilsTest.cpp - Unit tests the Orc RPC utils ----------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "llvm/ExecutionEngine/Orc/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>)> { 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(); } 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) { 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"; 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}); 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); 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(); }