blob: 3f25776e14167794a8321a73d617769a1b8ba56d [file] [log] [blame]
#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() {}
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;
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>());
}