#include <errno.h> #include <fcntl.h> #include <unistd.h> #include <array> #include <cstdint> #include <memory> #include <numeric> #include <string> #include <thread> #include <gtest/gtest.h> #include <pdx/channel_handle.h> #include <pdx/client.h> #include <pdx/rpc/remote_method.h> #include <pdx/rpc/serializable.h> #include <pdx/service.h> #include <pdx/service_dispatcher.h> #include <uds/client_channel.h> #include <uds/client_channel_factory.h> #include <uds/service_endpoint.h> using android::pdx::BorrowedHandle; using android::pdx::Channel; using android::pdx::ClientBase; using android::pdx::ErrorStatus; using android::pdx::LocalChannelHandle; using android::pdx::LocalHandle; using android::pdx::Message; using android::pdx::RemoteChannelHandle; using android::pdx::RemoteHandle; using android::pdx::ServiceBase; using android::pdx::ServiceDispatcher; using android::pdx::Status; using android::pdx::uds::Endpoint; using namespace android::pdx::rpc; namespace { std::string Rot13(const std::string& s) { std::string text = s; std::transform(std::begin(text), std::end(text), std::begin(text), [](char c) -> char { if (!std::isalpha(c)) { return c; } else { const char pivot = std::isupper(c) ? 'A' : 'a'; return (c - pivot + 13) % 26 + pivot; } }); return text; } // Defines a serializable user type that may be transferred between client and // service. struct TestType { int a; float b; std::string c; TestType() {} TestType(int a, float b, const std::string& c) : a(a), b(b), c(c) {} // Make gtest expressions simpler by defining equality operator. This is not // needed for serialization. bool operator==(const TestType& other) const { return a == other.a && b == other.b && c == other.c; } private: PDX_SERIALIZABLE_MEMBERS(TestType, a, b, c); }; struct DerivedTestType : public TestType { DerivedTestType() : TestType() {} DerivedTestType(int a, float b) : TestType(a, b, "constant") {} }; // Defines a serializable user type with a LocalHandle member. struct TestFdType { int a; LocalHandle fd; TestFdType() {} TestFdType(int a, LocalHandle fd) : a(a), fd(std::move(fd)) {} private: PDX_SERIALIZABLE_MEMBERS(TestFdType, a, fd); }; // Defines a serializable user template type with a FileHandle member. template <typename FileHandleType> struct TestTemplateType { FileHandleType fd; TestTemplateType() {} explicit TestTemplateType(FileHandleType fd) : fd(std::move(fd)) {} private: PDX_SERIALIZABLE_MEMBERS(TestTemplateType<FileHandleType>, fd); }; struct BasicStruct { int a; int b; std::string c; private: PDX_SERIALIZABLE_MEMBERS(BasicStruct, a, b, c); }; using BasicStructTraits = SerializableTraits<BasicStruct>; struct NonSerializableType { int a; int b; std::string c; }; struct IncorrectlyDefinedSerializableType { int a; int b; private: using SerializableMembers = std::tuple<int, int>; }; // Defines the contract between the client and service, including ServiceFS // endpoint path, method opcodes, and remote method signatures. struct TestInterface final { // Service path. static constexpr char kClientPath[] = "socket_test"; // Op codes. enum { kOpAdd = 0, kOpFoo, kOpConcatenate, kOpWriteBuffer, kOpStringLength, kOpSendTestType, kOpSendBasicStruct, kOpSendVector, kOpRot13, kOpNoArgs, kOpSendFile, kOpGetFile, kOpGetTestFdType, kOpOpenFiles, kOpReadFile, kOpPushChannel, kOpPositive, }; // Methods. PDX_REMOTE_METHOD(Add, kOpAdd, int(int, int)); PDX_REMOTE_METHOD(Foo, kOpFoo, int(int, const std::string&)); PDX_REMOTE_METHOD(Concatenate, kOpConcatenate, std::string(const std::string&, const std::string&)); PDX_REMOTE_METHOD(SumVector, kOpWriteBuffer, int(const std::vector<int>&)); PDX_REMOTE_METHOD(StringLength, kOpStringLength, int(const std::string&)); PDX_REMOTE_METHOD(SendTestType, kOpSendTestType, TestType(const TestType&)); PDX_REMOTE_METHOD(SendBasicStruct, kOpSendBasicStruct, BasicStruct(const BasicStruct&)); PDX_REMOTE_METHOD(SendVector, kOpSendVector, std::string(const std::vector<TestType>&)); PDX_REMOTE_METHOD(Rot13, kOpRot13, std::string(const std::string&)); PDX_REMOTE_METHOD(NoArgs, kOpNoArgs, int(Void)); PDX_REMOTE_METHOD(SendFile, kOpSendFile, int(const LocalHandle& fd)); PDX_REMOTE_METHOD(GetFile, kOpGetFile, LocalHandle(const std::string&, int)); PDX_REMOTE_METHOD(GetTestFdType, kOpGetTestFdType, TestFdType(int, const std::string&, int)); PDX_REMOTE_METHOD(OpenFiles, kOpOpenFiles, std::vector<LocalHandle>( const std::vector<std::pair<std::string, int>>&)); PDX_REMOTE_METHOD(ReadFile, kOpReadFile, std::pair<int, BufferWrapper<std::uint8_t*>>( const std::string&, int, std::size_t)); PDX_REMOTE_METHOD(PushChannel, kOpPushChannel, LocalChannelHandle(Void)); PDX_REMOTE_METHOD(Positive, kOpPositive, void(int)); PDX_REMOTE_API(API, Add, Foo, Concatenate, SumVector, StringLength, SendTestType, SendVector, Rot13, NoArgs, SendFile, GetFile, GetTestFdType, OpenFiles, PushChannel, Positive); }; constexpr char TestInterface::kClientPath[]; // Test client to send messages to the test service. class TestClient : public ClientBase<TestClient> { public: int Add(int a, int b) { return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::Add>(a, b)); } int Foo(int a, const std::string& b) { return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::Foo>(a, b)); } std::string Concatenate(const std::string& a, const std::string& b) { std::string return_value; Status<std::string> status = InvokeRemoteMethod<TestInterface::Concatenate>(a, b); if (!status) return std::string("[Error]"); else return status.take(); } int SumVector(const int* buffer, std::size_t size) { return ReturnStatusOrError( InvokeRemoteMethod<TestInterface::SumVector>(WrapArray(buffer, size))); } int SumVector(const std::vector<int>& buffer) { return ReturnStatusOrError( InvokeRemoteMethod<TestInterface::SumVector>(buffer)); } int StringLength(const char* string, std::size_t size) { return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::StringLength>( WrapString(string, size))); } int StringLength(const std::string& string) { return ReturnStatusOrError( InvokeRemoteMethod<TestInterface::StringLength>(string)); } TestType SendTestType(const TestType& tt) { Status<TestType> status = InvokeRemoteMethod<TestInterface::SendTestType>(tt); if (!status) return TestType(0, 0.0, "[Error]"); else return status.take(); } BasicStruct SendBasicStruct(const BasicStruct& bs) { Status<BasicStruct> status = InvokeRemoteMethod<TestInterface::SendBasicStruct>(bs); if (!status) return BasicStruct{0, 0, "[Error]"}; else return status.take(); } std::string SendVector(const std::vector<TestType>& v) { Status<std::string> status = InvokeRemoteMethod<TestInterface::SendVector>(v); if (!status) return "[Error]"; else return status.take(); } std::string Rot13(const std::string& string) { Status<std::string> status = InvokeRemoteMethod<TestInterface::Rot13>(string); return status ? status.get() : string; } int NoArgs() { return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::NoArgs>()); } int SendFile(const LocalHandle& fd) { return ReturnStatusOrError(InvokeRemoteMethod<TestInterface::SendFile>(fd)); } LocalHandle GetFile(const std::string& path, int mode) { Status<LocalHandle> status = InvokeRemoteMethod<TestInterface::GetFile>(path, mode); if (!status) return LocalHandle(-status.error()); else return status.take(); } int GetFile(const std::string& path, int mode, LocalHandle* fd_out) { Status<void> status = InvokeRemoteMethodInPlace<TestInterface::GetFile>(fd_out, path, mode); return status ? 0 : -status.error(); } TestFdType GetTestFdType(int a, const std::string& path, int mode) { Status<TestFdType> status = InvokeRemoteMethod<TestInterface::GetTestFdType>(a, path, mode); if (!status) return {}; else return status.take(); } std::vector<LocalHandle> OpenFiles( const std::vector<std::pair<std::string, int>>& file_specs) { Status<std::vector<LocalHandle>> status = InvokeRemoteMethod<TestInterface::OpenFiles>(file_specs); if (!status) return {}; else return status.take(); } int ReadFile(void* buffer, std::size_t size, const std::string& path, int mode) { auto buffer_wrapper = WrapBuffer(buffer, size); auto return_value = std::make_pair(-1, buffer_wrapper); Status<void> status = InvokeRemoteMethodInPlace<TestInterface::ReadFile>( &return_value, path, mode, size); return status ? return_value.first : -status.error(); } int PushChannel(LocalChannelHandle* fd_out) { auto status = InvokeRemoteMethodInPlace<TestInterface::PushChannel>(fd_out); return status ? 0 : -status.error(); } bool Positive(int test_value) { auto status = InvokeRemoteMethod<TestInterface::Positive>(test_value); return status.ok(); } int GetFd() const { return event_fd(); } private: friend BASE; explicit TestClient(LocalChannelHandle channel_handle) : BASE{android::pdx::uds::ClientChannel::Create( std::move(channel_handle))} {} TestClient() : BASE{android::pdx::uds::ClientChannelFactory::Create( TestInterface::kClientPath)} {} TestClient(const TestClient&) = delete; void operator=(const TestClient&) = delete; }; // Test service that encodes/decodes messages from clients. class TestService : public ServiceBase<TestService> { public: Status<void> HandleMessage(Message& message) override { switch (message.GetOp()) { case TestInterface::Add::Opcode: DispatchRemoteMethod<TestInterface::Add>(*this, &TestService::OnAdd, message); return {}; case TestInterface::Foo::Opcode: DispatchRemoteMethod<TestInterface::Foo>(*this, &TestService::OnFoo, message); return {}; case TestInterface::Concatenate::Opcode: DispatchRemoteMethod<TestInterface::Concatenate>( *this, &TestService::OnConcatenate, message); return {}; case TestInterface::SumVector::Opcode: DispatchRemoteMethod<TestInterface::SumVector>( *this, &TestService::OnSumVector, message); return {}; case TestInterface::StringLength::Opcode: DispatchRemoteMethod<TestInterface::StringLength>( *this, &TestService::OnStringLength, message); return {}; case TestInterface::SendTestType::Opcode: DispatchRemoteMethod<TestInterface::SendTestType>( *this, &TestService::OnSendTestType, message); return {}; case TestInterface::SendVector::Opcode: DispatchRemoteMethod<TestInterface::SendVector>( *this, &TestService::OnSendVector, message); return {}; case TestInterface::Rot13::Opcode: DispatchRemoteMethod<TestInterface::Rot13>(*this, &TestService::OnRot13, message); return {}; case TestInterface::NoArgs::Opcode: DispatchRemoteMethod<TestInterface::NoArgs>( *this, &TestService::OnNoArgs, message); return {}; case TestInterface::SendFile::Opcode: DispatchRemoteMethod<TestInterface::SendFile>( *this, &TestService::OnSendFile, message); return {}; case TestInterface::GetFile::Opcode: DispatchRemoteMethod<TestInterface::GetFile>( *this, &TestService::OnGetFile, message); return {}; case TestInterface::GetTestFdType::Opcode: DispatchRemoteMethod<TestInterface::GetTestFdType>( *this, &TestService::OnGetTestFdType, message); return {}; case TestInterface::OpenFiles::Opcode: DispatchRemoteMethod<TestInterface::OpenFiles>( *this, &TestService::OnOpenFiles, message); return {}; case TestInterface::ReadFile::Opcode: DispatchRemoteMethod<TestInterface::ReadFile>( *this, &TestService::OnReadFile, message); return {}; case TestInterface::PushChannel::Opcode: DispatchRemoteMethod<TestInterface::PushChannel>( *this, &TestService::OnPushChannel, message); return {}; case TestInterface::Positive::Opcode: DispatchRemoteMethod<TestInterface::Positive>( *this, &TestService::OnPositive, message); return {}; default: return Service::DefaultHandleMessage(message); } } private: friend BASE; TestService() : BASE("TestService", Endpoint::CreateAndBindSocket(TestInterface::kClientPath)) {} int OnAdd(Message&, int a, int b) { return a + b; } int OnFoo(Message&, int a, const std::string& b) { return a + b.length(); } std::string OnConcatenate(Message&, const std::string& a, const std::string& b) { return a + b; } int OnSumVector(Message&, const std::vector<int>& vector) { return std::accumulate(vector.begin(), vector.end(), 0); } int OnStringLength(Message&, const std::string& string) { return string.length(); } TestType OnSendTestType(Message&, const TestType& tt) { return TestType(tt.a + 20, tt.b - 2.0, tt.c + "foo"); } std::string OnSendVector(Message&, const std::vector<TestType>& v) { std::string return_value = ""; for (const auto& tt : v) return_value += tt.c; return return_value; } Status<std::string> OnRot13(Message&, const std::string& s) { return {Rot13(s)}; } int OnNoArgs(Message&) { return 1; } int OnSendFile(Message&, const LocalHandle& fd) { return fd.Get(); } LocalHandle OnGetFile(Message& message, const std::string& path, int mode) { LocalHandle fd(path.c_str(), mode); if (!fd) message.ReplyError(errno); return fd; } TestFdType OnGetTestFdType(Message& message, int a, const std::string& path, int mode) { TestFdType return_value(a, LocalHandle(path, mode)); if (!return_value.fd) message.ReplyError(errno); return return_value; } std::vector<LocalHandle> OnOpenFiles( Message&, const std::vector<std::pair<std::string, int>>& file_specs) { std::vector<LocalHandle> return_value; for (auto& spec : file_specs) { LocalHandle fd(spec.first, spec.second); if (fd) return_value.emplace_back(std::move(fd)); else return_value.emplace_back(-errno); } return return_value; } std::pair<int, BufferWrapper<std::vector<std::uint8_t>>> OnReadFile( Message& message, const std::string& path, int mode, std::size_t length) { std::pair<int, BufferWrapper<std::vector<std::uint8_t>>> return_value; LocalHandle fd(path, mode); if (!fd) { message.ReplyError(errno); return return_value; } return_value.second.reserve(length); const int ret = read(fd.Get(), return_value.second.data(), length); if (ret < 0) { message.ReplyError(errno); return return_value; } return_value.second.resize(ret); return_value.first = ret; return return_value; } RemoteChannelHandle OnPushChannel(Message& message) { auto status = message.PushChannel(0, nullptr, nullptr); if (!status) { message.ReplyError(status.error()); return {}; } return status.take(); } Status<void> OnPositive(Message& /*message*/, int test_value) { if (test_value >= 0) return {}; else return ErrorStatus(EINVAL); } TestService(const TestService&) = delete; void operator=(const TestService&) = delete; }; } // anonymous namespace // Use a test fixture to ensure proper order of cleanup between clients, // services, and the dispatcher. As these objects are cleaned up in the same // thread, either the service or client must be destroyed before stopping the // dispatcher. The reason for this is that clients send blocking "close" // messages to their respective services on destruction. If this happens after // stopping the dispatcher the client destructor will get blocked waiting for a // reply that will never come. In normal use of the service framework this is // never an issue because clients and the dispatcher for the same service are // never destructed in the same thread (they live in different processes). class RemoteMethodTest : public ::testing::Test { protected: std::unique_ptr<ServiceDispatcher> dispatcher_; std::thread dispatch_thread_; void SetUp() override { // Create a dispatcher to handle messages to services. dispatcher_ = android::pdx::ServiceDispatcher::Create(); ASSERT_NE(nullptr, dispatcher_); // Start the message dispatch loop in a separate thread. dispatch_thread_ = std::thread( std::bind(&ServiceDispatcher::EnterDispatchLoop, dispatcher_.get())); } void TearDown() override { if (dispatcher_) { // Cancel the dispatcher and wait for the thread to terminate. // Explicitly // join the thread so that destruction doesn't deallocate the // dispatcher // before the thread finishes. dispatcher_->SetCanceled(true); dispatch_thread_.join(); } } }; // Test basic operation of TestService/TestClient classes. TEST_F(RemoteMethodTest, BasicClientService) { // Create a test service and add it to the dispatcher. auto service = TestService::Create(); ASSERT_NE(nullptr, service); ASSERT_EQ(0, dispatcher_->AddService(service)); // Create a client to service. auto client = TestClient::Create(); ASSERT_NE(nullptr, client); const int sum = client->Add(10, 25); EXPECT_GE(35, sum); const auto cat = client->Concatenate("This is a string", ", that it is."); EXPECT_EQ("This is a string, that it is.", cat); std::string alphabet = "abcdefghijklmnopqrstuvwxyz"; const auto rot13_alphabet = client->Rot13(alphabet); EXPECT_EQ(Rot13(alphabet), rot13_alphabet); const auto length = client->Foo(10, "123"); EXPECT_EQ(13, length); const std::vector<int> vector{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; const int vector_sum = client->SumVector(vector.data(), vector.size()); const int vector_sum2 = client->SumVector(vector); EXPECT_EQ(std::accumulate(vector.begin(), vector.end(), 0), vector_sum); EXPECT_EQ(std::accumulate(vector.begin(), vector.end(), 0), vector_sum2); const auto string_length1 = client->StringLength("This is a string"); EXPECT_EQ(16, string_length1); const auto string_length2 = client->StringLength("1234567890"); EXPECT_EQ(10, string_length2); std::string string = "1234567890"; const auto string_length3 = client->StringLength(string.c_str(), string.length()); EXPECT_EQ(10, string_length3); TestType tt{10, 0.0, "string"}; const auto tt_result = client->SendTestType(tt); EXPECT_EQ(TestType(30, -2.0, "stringfoo"), tt_result); std::vector<TestType> ttv = {TestType(0, 0.0, "abc"), TestType(0, 0.0, "123")}; const std::string string_result = client->SendVector(ttv); EXPECT_EQ("abc123", string_result); const int int_result = client->NoArgs(); EXPECT_EQ(1, int_result); } TEST_F(RemoteMethodTest, LocalHandle) { // Create a test service and add it to the dispatcher. auto service = TestService::Create(); ASSERT_NE(nullptr, service); ASSERT_EQ(0, dispatcher_->AddService(service)); // Create a client to service. auto client = TestClient::Create(); ASSERT_NE(nullptr, client); LocalHandle fd("/dev/zero", O_RDONLY); ASSERT_TRUE(fd.IsValid()); int fd_result = client->SendFile(fd); EXPECT_LE(0, fd_result); EXPECT_NE(fd.Get(), fd_result); fd = LocalHandle(-3); fd_result = client->SendFile(fd); EXPECT_EQ(fd.Get(), fd_result); fd = client->GetFile("/dev/zero", O_RDONLY); ASSERT_TRUE(fd.IsValid()) << "Error code: " << fd.Get(); std::array<uint8_t, 10> buffer; buffer.fill(1); EXPECT_EQ(10, read(fd.Get(), buffer.data(), buffer.size())); EXPECT_EQ(buffer, decltype(buffer){{0}}); fd.Close(); const int error = client->GetFile("/dev/zero", O_RDONLY, &fd); EXPECT_EQ(0, error); EXPECT_TRUE(fd.IsValid()); buffer.fill(1); EXPECT_EQ(10, read(fd.Get(), buffer.data(), buffer.size())); EXPECT_EQ(buffer, decltype(buffer){{0}}); /* Seg fault. fd = client->GetFile("/dev/foobar", O_RDONLY); EXPECT_FALSE(fd.IsValid()); */ } TEST_F(RemoteMethodTest, PushChannel) { // Create a test service and add it to the dispatcher. auto service = TestService::Create(); ASSERT_NE(nullptr, service); ASSERT_EQ(0, dispatcher_->AddService(service)); // Create a client to service. auto client = TestClient::Create(); ASSERT_NE(nullptr, client); // Get a new channel as an fd. LocalChannelHandle channel; const int ret = client->PushChannel(&channel); EXPECT_EQ(0, ret); EXPECT_TRUE(channel.valid()); // Create a new client from the channel. auto client2 = TestClient::Create(std::move(channel)); ASSERT_NE(nullptr, client2); // Test that the new channel works. const int sum = client2->Add(10, 25); EXPECT_GE(35, sum); } TEST_F(RemoteMethodTest, Positive) { // Create a test service and add it to the dispatcher. auto service = TestService::Create(); ASSERT_NE(nullptr, service); ASSERT_EQ(0, dispatcher_->AddService(service)); // Create a client to service. auto client = TestClient::Create(); ASSERT_NE(nullptr, client); ASSERT_TRUE(client->Positive(0)); ASSERT_TRUE(client->Positive(1)); ASSERT_FALSE(client->Positive(-1)); } TEST_F(RemoteMethodTest, AggregateLocalHandle) { // Create a test service and add it to the dispatcher. auto service = TestService::Create(); ASSERT_NE(nullptr, service); ASSERT_EQ(0, dispatcher_->AddService(service)); // Create a client to service. auto client = TestClient::Create(); ASSERT_NE(nullptr, client); TestFdType result = client->GetTestFdType(10, "/dev/zero", O_RDONLY); EXPECT_TRUE(result.fd.IsValid()); EXPECT_EQ(10, result.a); std::vector<LocalHandle> files = client->OpenFiles({{{"/dev/zero", O_RDONLY}, {"/dev/null", O_WRONLY}, {"/dev/zero", O_RDONLY}}}); ASSERT_EQ(3u, files.size()); EXPECT_TRUE(files[0].IsValid()); EXPECT_TRUE(files[1].IsValid()); EXPECT_TRUE(files[2].IsValid()); } TEST_F(RemoteMethodTest, BufferWrapper) { // Create a test service and add it to the dispatcher. auto service = TestService::Create(); ASSERT_NE(nullptr, service); ASSERT_EQ(0, dispatcher_->AddService(service)); // Create a client to service. auto client = TestClient::Create(); ASSERT_NE(nullptr, client); const int buffer_size = 20; std::vector<std::uint8_t> buffer(buffer_size, 'x'); std::vector<std::uint8_t> expected(buffer_size, 0); int ret = client->ReadFile(buffer.data(), buffer.size(), "/dev/zero", O_RDONLY); EXPECT_EQ(buffer_size, ret); EXPECT_EQ(expected, buffer); } // // RemoteMethodFramework: Tests the type-based framework that remote method // support is built upon. // // Test logical And template. TEST(RemoteMethodFramework, And) { EXPECT_TRUE((And<std::true_type, std::true_type>::value)); EXPECT_FALSE((And<std::true_type, std::false_type>::value)); EXPECT_FALSE((And<std::false_type, std::true_type>::value)); EXPECT_FALSE((And<std::false_type, std::false_type>::value)); EXPECT_TRUE((And<std::true_type, std::true_type, std::true_type>::value)); EXPECT_FALSE((And<std::true_type, std::true_type, std::false_type>::value)); EXPECT_FALSE((And<std::true_type, std::false_type, std::true_type>::value)); EXPECT_FALSE((And<std::true_type, std::false_type, std::false_type>::value)); EXPECT_FALSE((And<std::false_type, std::true_type, std::true_type>::value)); EXPECT_FALSE((And<std::false_type, std::true_type, std::false_type>::value)); EXPECT_FALSE((And<std::false_type, std::false_type, std::true_type>::value)); EXPECT_FALSE((And<std::false_type, std::false_type, std::false_type>::value)); } // Test convertible type constraints. TEST(RemoteMethodFramework, IsConvertible) { // std::pair. EXPECT_TRUE( (IsConvertible<std::pair<int, float>, std::pair<int, float>>::value)); EXPECT_FALSE( (IsConvertible<std::pair<int, float>, std::pair<float, float>>::value)); EXPECT_FALSE( (IsConvertible<std::pair<int, float>, std::pair<float, int>>::value)); // Nested std::pair. EXPECT_TRUE((IsConvertible<std::pair<std::pair<int, float>, float>, std::pair<std::pair<int, float>, float>>::value)); EXPECT_FALSE((IsConvertible<std::pair<std::pair<int, float>, float>, std::pair<std::pair<float, int>, float>>::value)); // std::tuple and std::pair. EXPECT_TRUE( (IsConvertible<std::pair<int, float>, std::tuple<int, float>>::value)); EXPECT_TRUE( (IsConvertible<std::tuple<int, float>, std::pair<int, float>>::value)); EXPECT_FALSE( (IsConvertible<std::pair<float, float>, std::tuple<int, float>>::value)); EXPECT_FALSE( (IsConvertible<std::tuple<float, float>, std::pair<int, float>>::value)); EXPECT_FALSE( (IsConvertible<std::pair<int, int>, std::tuple<int, float>>::value)); EXPECT_FALSE( (IsConvertible<std::tuple<int, int>, std::pair<int, float>>::value)); EXPECT_FALSE( (IsConvertible<std::pair<int, int>, std::tuple<int, int, int>>::value)); EXPECT_FALSE( (IsConvertible<std::tuple<int, int, int>, std::pair<int, int>>::value)); // std::vector. EXPECT_TRUE((IsConvertible<std::vector<int>, std::vector<int>>::value)); EXPECT_FALSE((IsConvertible<std::vector<int>, std::vector<float>>::value)); // Nested std::vector. EXPECT_TRUE((IsConvertible<std::vector<std::pair<int, int>>, std::vector<std::pair<int, int>>>::value)); EXPECT_FALSE((IsConvertible<std::vector<std::pair<int, int>>, std::vector<std::pair<int, float>>>::value)); EXPECT_FALSE((IsConvertible<std::vector<std::pair<int, int>>, std::vector<std::pair<float, int>>>::value)); EXPECT_FALSE((IsConvertible<std::vector<std::pair<int, int>>, std::vector<std::pair<float, float>>>::value)); // std::vector with nested convertible types. EXPECT_TRUE((IsConvertible<std::vector<StringWrapper<char>>, std::vector<std::string>>::value)); // std::map and std::unordered_map. EXPECT_TRUE((IsConvertible<std::map<int, float>, std::unordered_map<int, float>>::value)); EXPECT_FALSE((IsConvertible<std::map<float, float>, std::unordered_map<int, float>>::value)); EXPECT_FALSE((IsConvertible<std::map<float, float>, std::unordered_map<float, int>>::value)); EXPECT_FALSE((IsConvertible<std::map<float, float>, std::unordered_map<int, int>>::value)); EXPECT_TRUE((IsConvertible<std::unordered_map<int, float>, std::map<int, float>>::value)); EXPECT_FALSE((IsConvertible<std::unordered_map<float, float>, std::map<int, float>>::value)); EXPECT_FALSE((IsConvertible<std::unordered_map<float, float>, std::map<float, int>>::value)); EXPECT_FALSE((IsConvertible<std::unordered_map<float, float>, std::map<int, int>>::value)); // std::map with nested convertible types. EXPECT_TRUE((IsConvertible<std::map<int, std::string>, std::map<int, StringWrapper<char>>>::value)); EXPECT_TRUE( (IsConvertible<std::map<std::tuple<int, int>, std::string>, std::map<std::pair<int, int>, std::string>>::value)); // std::unordered_map with nested convertible types. EXPECT_TRUE( (IsConvertible<std::unordered_map<int, std::string>, std::unordered_map<int, StringWrapper<char>>>::value)); EXPECT_TRUE((IsConvertible< std::unordered_map<std::tuple<int, int>, std::string>, std::unordered_map<std::pair<int, int>, std::string>>::value)); // std::string. EXPECT_TRUE((IsConvertible<std::string, std::string>::value)); EXPECT_FALSE((IsConvertible<std::string, int>::value)); EXPECT_FALSE((IsConvertible<int, std::string>::value)); // Nested std::string. EXPECT_TRUE((IsConvertible<std::pair<std::string, std::string>, std::pair<std::string, std::string>>::value)); EXPECT_FALSE((IsConvertible<std::pair<std::string, std::string>, std::pair<std::string, int>>::value)); EXPECT_FALSE((IsConvertible<std::pair<std::string, std::string>, std::pair<int, std::string>>::value)); EXPECT_FALSE((IsConvertible<std::pair<std::string, std::string>, std::pair<int, int>>::value)); // StringWrapper. EXPECT_TRUE((IsConvertible<StringWrapper<char>, StringWrapper<char>>::value)); EXPECT_TRUE((IsConvertible<StringWrapper<char>, std::string>::value)); EXPECT_TRUE((IsConvertible<std::string, StringWrapper<char>>::value)); EXPECT_FALSE((IsConvertible<StringWrapper<char>, int>::value)); EXPECT_FALSE( (IsConvertible<StringWrapper<char>, BufferWrapper<char*>>::value)); // BufferWrapper. EXPECT_TRUE( (IsConvertible<BufferWrapper<char*>, BufferWrapper<char*>>::value)); EXPECT_TRUE( (IsConvertible<BufferWrapper<char*>, BufferWrapper<const char*>>::value)); EXPECT_FALSE( (IsConvertible<BufferWrapper<char*>, BufferWrapper<int*>>::value)); EXPECT_TRUE((IsConvertible<BufferWrapper<char*>, BufferWrapper<std::vector<char>>>::value)); // RemoteHandle and BorrowedHandle. EXPECT_TRUE((IsConvertible<LocalHandle, RemoteHandle>::value)); EXPECT_TRUE((IsConvertible<LocalHandle, BorrowedHandle>::value)); // Test rewriting user defined types. EXPECT_TRUE((IsConvertible<TestTemplateType<LocalHandle>, TestTemplateType<RemoteHandle>>::value)); EXPECT_TRUE((IsConvertible<TestTemplateType<LocalHandle>, TestTemplateType<BorrowedHandle>>::value)); EXPECT_FALSE((IsConvertible<TestTemplateType<RemoteHandle>, TestTemplateType<LocalHandle>>::value)); EXPECT_FALSE((IsConvertible<TestTemplateType<BorrowedHandle>, TestTemplateType<LocalHandle>>::value)); // TODO(eieio): More thorough testing of convertible types. } TEST(RemoteMethodFramework, SerializableMembers) { EXPECT_TRUE(HasSerializableMembers<TestTemplateType<LocalHandle>>::value); EXPECT_TRUE(HasSerializableMembers<TestTemplateType<RemoteHandle>>::value); EXPECT_TRUE(HasSerializableMembers<TestTemplateType<BorrowedHandle>>::value); EXPECT_TRUE(std::is_void<EnableIfHasSerializableMembers< TestTemplateType<LocalHandle>>>::value); EXPECT_TRUE(std::is_void<EnableIfHasSerializableMembers< TestTemplateType<RemoteHandle>>>::value); EXPECT_TRUE(std::is_void<EnableIfHasSerializableMembers< TestTemplateType<BorrowedHandle>>>::value); EXPECT_TRUE(HasSerializableMembers<DerivedTestType>::value); EXPECT_TRUE(HasSerializableMembers<BasicStruct>::value); EXPECT_TRUE(HasSerializableMembers<TestType>::value); EXPECT_TRUE(HasSerializableMembers<TestTemplateType<LocalHandle>>::value); EXPECT_TRUE(HasSerializableMembers<TestTemplateType<RemoteHandle>>::value); EXPECT_TRUE(HasSerializableMembers<TestTemplateType<BorrowedHandle>>::value); EXPECT_TRUE(HasSerializableMembers<DerivedTestType>::value); EXPECT_FALSE(HasSerializableMembers<NonSerializableType>::value); EXPECT_FALSE( HasSerializableMembers<IncorrectlyDefinedSerializableType>::value); } TEST(RemoteMethodFramework, RemoteAPITypes) { EXPECT_EQ(0u, TestInterface::API::MethodIndex<TestInterface::Add>()); }