#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 <uds/client_channel.h>
#include <uds/client_channel_factory.h>
#include <uds/service_dispatcher.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::uds::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>());
}
