blob: 27427162f50a5574e2a50266bd2176b76fd4bc5d [file] [log] [blame]
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <unistd.h>
#include <array>
#include <atomic>
#include <memory>
#include <numeric>
#include <string>
#include <thread>
#include <gtest/gtest.h>
#include <pdx/channel_handle.h>
#include <pdx/client.h>
#include <pdx/file_handle.h>
#include <pdx/service.h>
#include <pdx/service_dispatcher.h>
#include <private/android_filesystem_config.h>
#include <uds/client_channel.h>
#include <uds/client_channel_factory.h>
#include <uds/service_endpoint.h>
using android::pdx::BorrowedChannelHandle;
using android::pdx::Channel;
using android::pdx::ChannelReference;
using android::pdx::ClientBase;
using android::pdx::ErrorStatus;
using android::pdx::LocalChannelHandle;
using android::pdx::LocalHandle;
using android::pdx::Message;
using android::pdx::MessageInfo;
using android::pdx::RemoteChannelHandle;
using android::pdx::ServiceBase;
using android::pdx::ServiceDispatcher;
using android::pdx::Status;
using android::pdx::Transaction;
using android::pdx::uds::Endpoint;
namespace {
const size_t kLargeDataSize = 100000;
const char kTestServicePath[] = "socket_test";
const char kTestService1[] = "1";
const char kTestService2[] = "2";
enum test_op_codes {
TEST_OP_GET_SERVICE_ID,
TEST_OP_SET_TEST_CHANNEL,
TEST_OP_GET_THIS_CHANNEL_ID,
TEST_OP_GET_TEST_CHANNEL_ID,
TEST_OP_CHECK_CHANNEL_ID,
TEST_OP_CHECK_CHANNEL_OBJECT,
TEST_OP_CHECK_CHANNEL_FROM_OTHER_SERVICE,
TEST_OP_GET_NEW_CHANNEL,
TEST_OP_GET_NEW_CHANNEL_FROM_OTHER_SERVICE,
TEST_OP_GET_THIS_PROCESS_ID,
TEST_OP_GET_THIS_THREAD_ID,
TEST_OP_GET_THIS_EUID,
TEST_OP_GET_THIS_EGID,
TEST_OP_IMPULSE,
TEST_OP_POLLHUP_FROM_SERVICE,
TEST_OP_POLLIN_FROM_SERVICE,
TEST_OP_SEND_LARGE_DATA_RETURN_SUM,
};
using ImpulsePayload = std::array<std::uint8_t, sizeof(MessageInfo::impulse)>;
// The test service creates a TestChannel for every client (channel) that
// connects. This represents the service-side context for each client.
class TestChannel : public Channel {
public:
explicit TestChannel(int channel_id) : channel_id_(channel_id) {}
int channel_id() const { return channel_id_; }
private:
friend class TestService;
int channel_id_;
TestChannel(const TestChannel&) = delete;
void operator=(const TestChannel&) = delete;
};
// Test service that creates a TestChannel for each channel and responds to test
// messages.
class TestService : public ServiceBase<TestService> {
public:
std::shared_ptr<Channel> OnChannelOpen(Message& message) override {
return std::make_shared<TestChannel>(message.GetChannelId());
}
void OnChannelClose(Message& /*message*/,
const std::shared_ptr<Channel>& channel) override {
if (test_channel_ == channel)
test_channel_ = nullptr;
}
void HandleImpulse(Message& message) override {
switch (message.GetOp()) {
case TEST_OP_SET_TEST_CHANNEL:
test_channel_ = message.GetChannel<TestChannel>();
break;
case TEST_OP_IMPULSE: {
impulse_payload_.fill(0);
std::copy(message.ImpulseBegin(), message.ImpulseEnd(),
impulse_payload_.begin());
break;
}
case TEST_OP_POLLHUP_FROM_SERVICE: {
message.ModifyChannelEvents(0, EPOLLHUP);
break;
}
}
}
Status<void> HandleMessage(Message& message) override {
switch (message.GetOp()) {
case TEST_OP_GET_SERVICE_ID:
REPLY_MESSAGE_RETURN(message, service_id_, {});
// Set the test channel to the TestChannel for the current channel. Other
// messages can use this to perform tests.
case TEST_OP_SET_TEST_CHANNEL:
test_channel_ = message.GetChannel<TestChannel>();
REPLY_MESSAGE_RETURN(message, 0, {});
// Return the channel id for the current channel.
case TEST_OP_GET_THIS_CHANNEL_ID:
REPLY_MESSAGE_RETURN(message, message.GetChannelId(), {});
// Return the channel id for the test channel.
case TEST_OP_GET_TEST_CHANNEL_ID:
if (test_channel_)
REPLY_MESSAGE_RETURN(message, test_channel_->channel_id(), {});
else
REPLY_ERROR_RETURN(message, ENOENT, {});
// Test check channel feature.
case TEST_OP_CHECK_CHANNEL_ID: {
ChannelReference ref = 0;
if (!message.ReadAll(&ref, sizeof(ref)))
REPLY_ERROR_RETURN(message, EIO, {});
const Status<int> ret = message.CheckChannel<TestChannel>(ref, nullptr);
REPLY_MESSAGE_RETURN(message, ret, {});
}
case TEST_OP_CHECK_CHANNEL_OBJECT: {
std::shared_ptr<TestChannel> channel;
ChannelReference ref = 0;
if (!message.ReadAll(&ref, sizeof(ref)))
REPLY_ERROR_RETURN(message, EIO, {});
const Status<int> ret =
message.CheckChannel<TestChannel>(ref, &channel);
if (!ret)
REPLY_MESSAGE_RETURN(message, ret, {});
if (channel != nullptr)
REPLY_MESSAGE_RETURN(message, channel->channel_id(), {});
else
REPLY_ERROR_RETURN(message, ENODATA, {});
}
case TEST_OP_CHECK_CHANNEL_FROM_OTHER_SERVICE: {
ChannelReference ref = 0;
if (!message.ReadAll(&ref, sizeof(ref)))
REPLY_ERROR_RETURN(message, EIO, {});
const Status<int> ret = message.CheckChannel<TestChannel>(
other_service_.get(), ref, nullptr);
REPLY_MESSAGE_RETURN(message, ret, {});
}
case TEST_OP_GET_NEW_CHANNEL: {
auto channel = std::make_shared<TestChannel>(-1);
Status<RemoteChannelHandle> channel_handle =
message.PushChannel(0, channel, &channel->channel_id_);
REPLY_MESSAGE_RETURN(message, channel_handle, {});
}
case TEST_OP_GET_NEW_CHANNEL_FROM_OTHER_SERVICE: {
if (!other_service_)
REPLY_ERROR_RETURN(message, EINVAL, {});
auto channel = std::make_shared<TestChannel>(-1);
Status<RemoteChannelHandle> channel_handle = message.PushChannel(
other_service_.get(), 0, channel, &channel->channel_id_);
REPLY_MESSAGE_RETURN(message, channel_handle, {});
}
case TEST_OP_GET_THIS_PROCESS_ID:
REPLY_MESSAGE_RETURN(message, message.GetProcessId(), {});
case TEST_OP_GET_THIS_THREAD_ID:
REPLY_MESSAGE_RETURN(message, message.GetThreadId(), {});
case TEST_OP_GET_THIS_EUID:
REPLY_MESSAGE_RETURN(message, message.GetEffectiveUserId(), {});
case TEST_OP_GET_THIS_EGID:
REPLY_MESSAGE_RETURN(message, message.GetEffectiveGroupId(), {});
case TEST_OP_POLLIN_FROM_SERVICE:
REPLY_MESSAGE_RETURN(message, message.ModifyChannelEvents(0, EPOLLIN),
{});
case TEST_OP_SEND_LARGE_DATA_RETURN_SUM: {
std::array<int, kLargeDataSize> data_array;
size_t size_to_read = data_array.size() * sizeof(int);
if (!message.ReadAll(data_array.data(), size_to_read)) {
REPLY_ERROR_RETURN(message, EIO, {});
}
int sum = std::accumulate(data_array.begin(), data_array.end(), 0);
REPLY_MESSAGE_RETURN(message, sum, {});
}
default:
return Service::DefaultHandleMessage(message);
}
}
const ImpulsePayload& GetImpulsePayload() const { return impulse_payload_; }
private:
friend BASE;
std::shared_ptr<TestChannel> test_channel_;
std::shared_ptr<TestService> other_service_;
int service_id_;
ImpulsePayload impulse_payload_;
static std::atomic<int> service_counter_;
TestService(const std::string& name,
const std::shared_ptr<TestService>& other_service)
: TestService(name, other_service, false) {}
TestService(const std::string& name,
const std::shared_ptr<TestService>& other_service, bool blocking)
: BASE(std::string("TestService") + name,
Endpoint::CreateAndBindSocket(kTestServicePath + name, blocking)),
other_service_(other_service),
service_id_(service_counter_++) {}
explicit TestService(const std::string& name) : TestService(name, nullptr) {}
TestService(const TestService&) = delete;
void operator=(const TestService&) = delete;
};
std::atomic<int> TestService::service_counter_;
// Test client to send messages to the test service.
class TestClient : public ClientBase<TestClient> {
public:
// Requests the service id of the service this channel is connected to.
int GetServiceId() {
Transaction trans{*this};
return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_SERVICE_ID));
}
// Requests the test channel to be set to this client's channel.
int SetTestChannel() {
Transaction trans{*this};
return ReturnStatusOrError(trans.Send<int>(TEST_OP_SET_TEST_CHANNEL));
}
// Request the test channel to be set to this client's channel using an async
// message.
int SetTestChannelAsync() {
return ReturnStatusOrError(SendImpulse(TEST_OP_SET_TEST_CHANNEL));
}
// Sends a test async message with payload.
int SendAsync(const void* buffer, size_t length) {
Transaction trans{*this};
return ReturnStatusOrError(SendImpulse(TEST_OP_IMPULSE, buffer, length));
}
// Requests the channel id for this client.
int GetThisChannelId() {
Transaction trans{*this};
return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_CHANNEL_ID));
}
// Requests the channel id of the test channel.
int GetTestChannelId() {
Transaction trans{*this};
return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_TEST_CHANNEL_ID));
}
// Checks whether the fd |channel_id| is a channel to the test service.
// Returns the channel id of the channel.
int CheckChannelIdArgument(BorrowedChannelHandle channel) {
Transaction trans{*this};
ChannelReference ref = trans.PushChannelHandle(channel).get();
return ReturnStatusOrError(trans.Send<int>(TEST_OP_CHECK_CHANNEL_ID, &ref,
sizeof(ref), nullptr, 0));
}
// Checks whether the fd |channel_id| is a channel to the test service.
// Returns the channel id of the channel exercising the context pointer.
int CheckChannelObjectArgument(BorrowedChannelHandle channel) {
Transaction trans{*this};
ChannelReference ref = trans.PushChannelHandle(channel).get();
return ReturnStatusOrError(trans.Send<int>(TEST_OP_CHECK_CHANNEL_OBJECT,
&ref, sizeof(ref), nullptr, 0));
}
// Checks whether the fd |channel_fd| is a channel to the other test service.
// Returns 0 on success.
int CheckChannelFromOtherService(BorrowedChannelHandle channel) {
Transaction trans{*this};
ChannelReference ref = trans.PushChannelHandle(channel).get();
return ReturnStatusOrError(
trans.Send<int>(TEST_OP_CHECK_CHANNEL_FROM_OTHER_SERVICE, &ref,
sizeof(ref), nullptr, 0));
}
// Requests a new channel to the service.
std::unique_ptr<TestClient> GetNewChannel() {
Transaction trans{*this};
auto status = trans.Send<LocalChannelHandle>(TEST_OP_GET_NEW_CHANNEL);
if (status)
return TestClient::Create(status.take());
else
return nullptr;
}
// Requests a new channel to the other service.
std::unique_ptr<TestClient> GetNewChannelFromOtherService() {
Transaction trans{*this};
auto status = trans.Send<LocalChannelHandle>(
TEST_OP_GET_NEW_CHANNEL_FROM_OTHER_SERVICE);
if (status)
return TestClient::Create(status.take());
else
return nullptr;
}
// Requests an id from the message description.
pid_t GetThisProcessId() {
Transaction trans{*this};
return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_PROCESS_ID));
}
pid_t GetThisThreadId() {
Transaction trans{*this};
return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_THREAD_ID));
}
uid_t GetThisEffectiveUserId() {
Transaction trans{*this};
return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_EUID));
}
gid_t GetThisEffectiveGroupId() {
Transaction trans{*this};
return ReturnStatusOrError(trans.Send<int>(TEST_OP_GET_THIS_EGID));
}
int SendPollHupEvent() {
return ReturnStatusOrError(SendImpulse(TEST_OP_POLLHUP_FROM_SERVICE));
}
int SendPollInEvent() {
Transaction trans{*this};
return ReturnStatusOrError(trans.Send<int>(TEST_OP_POLLIN_FROM_SERVICE));
}
int SendLargeDataReturnSum(
const std::array<int, kLargeDataSize>& data_array) {
Transaction trans{*this};
return ReturnStatusOrError(
trans.Send<int>(TEST_OP_SEND_LARGE_DATA_RETURN_SUM, data_array.data(),
data_array.size() * sizeof(int), nullptr, 0));
}
Status<int> GetEventMask(int events) {
if (auto* client_channel = GetChannel()) {
return client_channel->GetEventMask(events);
} else {
return ErrorStatus(EINVAL);
}
}
using ClientBase<TestClient>::event_fd;
enum : size_t { kMaxPayload = MAX_IMPULSE_LENGTH };
private:
friend BASE;
explicit TestClient(const std::string& name)
: BASE{android::pdx::uds::ClientChannelFactory::Create(kTestServicePath +
name)} {}
explicit TestClient(LocalChannelHandle channel)
: BASE{android::pdx::uds::ClientChannel::Create(std::move(channel))} {}
TestClient(const TestClient&) = delete;
void operator=(const TestClient&) = delete;
};
} // anonymous namespace
// Use a test fixture to ensure proper order of cleanup between clients,
// services, and the dispatcher. These objects are cleaned up in the same
// thread, order is important; either the service or the client must be
// destroyed before the dispatcher is stopped. The reason for this is that
// clients send blocking "close" messages to their respective services on
// destruction. If this happens after the dispatcher is stopped 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 ServiceFrameworkTest : 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(ServiceFrameworkTest, BasicClientService) {
// Create a test service and add it to the dispatcher.
auto service = TestService::Create(kTestService1);
ASSERT_NE(nullptr, service);
ASSERT_EQ(0, dispatcher_->AddService(service));
// Create a client to service.
auto client = TestClient::Create(kTestService1);
ASSERT_NE(nullptr, client);
// Get the channel id that will be returned by the next tests.
const int channel_id = client->GetThisChannelId();
EXPECT_LE(0, channel_id);
// Check return value before test channel is set.
EXPECT_EQ(-ENOENT, client->GetTestChannelId());
// Set test channel and perform the test again.
EXPECT_EQ(0, client->SetTestChannel());
EXPECT_EQ(channel_id, client->GetTestChannelId());
}
// Test impulses.
TEST_F(ServiceFrameworkTest, Impulse) {
// Create a test service and add it to the dispatcher.
auto service = TestService::Create(kTestService1);
ASSERT_NE(nullptr, service);
ASSERT_EQ(0, dispatcher_->AddService(service));
auto client = TestClient::Create(kTestService1);
ASSERT_NE(nullptr, client);
// Get the channel id that will be returned by the next tests.
const int channel_id = client->GetThisChannelId();
EXPECT_LE(0, channel_id);
// Check return value before test channel is set.
EXPECT_EQ(-ENOENT, client->GetTestChannelId());
// Set test channel with an impulse and perform the test again.
EXPECT_EQ(0, client->SetTestChannelAsync());
EXPECT_EQ(channel_id, client->GetTestChannelId());
ImpulsePayload expected_payload = {{'a', 'b', 'c'}};
EXPECT_EQ(0, client->SendAsync(expected_payload.data(), 3));
// Send a synchronous message to make sure the async message is handled before
// we check the payload.
client->GetThisChannelId();
EXPECT_EQ(expected_payload, service->GetImpulsePayload());
// Impulse payloads are limited to 4 machine words.
EXPECT_EQ(
0, client->SendAsync(expected_payload.data(), TestClient::kMaxPayload));
EXPECT_EQ(-EINVAL, client->SendAsync(expected_payload.data(),
TestClient::kMaxPayload + 1));
// Test invalid pointer.
const std::uint8_t* invalid_pointer = nullptr;
EXPECT_EQ(-EINVAL, client->SendAsync(invalid_pointer, sizeof(int)));
}
// Test impulses.
TEST_F(ServiceFrameworkTest, ImpulseHangup) {
// Create a test service and add it to the dispatcher.
auto service = TestService::Create(kTestService1);
ASSERT_NE(nullptr, service);
ASSERT_EQ(0, dispatcher_->AddService(service));
auto client = TestClient::Create(kTestService1);
ASSERT_NE(nullptr, client);
const int kMaxIterations = 1000;
for (int i = 0; i < kMaxIterations; i++) {
auto impulse_client = TestClient::Create(kTestService1);
ASSERT_NE(nullptr, impulse_client);
const uint8_t a = (i >> 0) & 0xff;
const uint8_t b = (i >> 8) & 0xff;
const uint8_t c = (i >> 16) & 0xff;
const uint8_t d = (i >> 24) & 0xff;
ImpulsePayload expected_payload = {{a, b, c, d}};
EXPECT_EQ(0, impulse_client->SendAsync(expected_payload.data(), 4));
// Hangup the impulse test client, then send a sync message over client to
// make sure the hangup message is handled before checking the impulse
// payload.
impulse_client = nullptr;
client->GetThisChannelId();
EXPECT_EQ(expected_payload, service->GetImpulsePayload());
}
}
// Test Message::PushChannel/Service::PushChannel API.
TEST_F(ServiceFrameworkTest, PushChannel) {
// Create a test service and add it to the dispatcher.
auto other_service = TestService::Create(kTestService1);
ASSERT_NE(nullptr, other_service);
ASSERT_EQ(0, dispatcher_->AddService(other_service));
// Create a second test service and add it to the dispatcher.
auto service = TestService::Create(kTestService2, other_service);
ASSERT_NE(nullptr, service);
ASSERT_EQ(0, dispatcher_->AddService(service));
// Create a client to the second test service.
auto client1 = TestClient::Create(kTestService2);
ASSERT_NE(nullptr, client1);
// Test the creation of new channels using the push APIs.
const int channel_id1 = client1->GetThisChannelId();
EXPECT_LE(0, channel_id1);
auto client2 = client1->GetNewChannel();
EXPECT_NE(nullptr, client2);
EXPECT_NE(client1->event_fd(), client2->event_fd());
const int channel_id2 = client2->GetThisChannelId();
EXPECT_LE(0, channel_id2);
EXPECT_NE(channel_id1, channel_id2);
auto client3 = client1->GetNewChannelFromOtherService();
EXPECT_NE(nullptr, client3);
EXPECT_NE(client1->event_fd(), client3->event_fd());
const int channel_id3 = client3->GetThisChannelId();
EXPECT_LE(0, channel_id3);
// Test which services the channels are connected to.
const int service_id1 = client1->GetServiceId();
EXPECT_LE(0, service_id1);
const int service_id2 = client2->GetServiceId();
EXPECT_LE(0, service_id2);
const int service_id3 = client3->GetServiceId();
EXPECT_LE(0, service_id3);
EXPECT_EQ(service_id1, service_id2);
EXPECT_NE(service_id1, service_id3);
}
// Tests process id, thread id, effective user id, and effective group id
// returned in the message description.
TEST_F(ServiceFrameworkTest, Ids) {
// Create a test service and add it to the dispatcher.
auto service = TestService::Create(kTestService1);
ASSERT_NE(nullptr, service);
ASSERT_EQ(0, dispatcher_->AddService(service));
// Create a client to service.
auto client = TestClient::Create(kTestService1);
ASSERT_NE(nullptr, client);
// Pids 0-2 are defined, no user task should have them.
const pid_t process_id1 = client->GetThisProcessId();
EXPECT_LT(2, process_id1);
pid_t process_id2;
std::thread thread([&]() { process_id2 = client->GetThisProcessId(); });
thread.join();
EXPECT_LT(2, process_id2);
EXPECT_EQ(process_id1, process_id2);
// This test must run as root for the rest of these tests to work.
const int euid1 = client->GetThisEffectiveUserId();
ASSERT_EQ(0, euid1);
const int egid1 = client->GetThisEffectiveGroupId();
EXPECT_EQ(0, egid1);
// Set effective uid/gid to system.
ASSERT_EQ(0, setegid(AID_SYSTEM));
ASSERT_EQ(0, seteuid(AID_SYSTEM));
const int euid2 = client->GetThisEffectiveUserId();
EXPECT_EQ(AID_SYSTEM, euid2);
const int egid2 = client->GetThisEffectiveGroupId();
EXPECT_EQ(AID_SYSTEM, egid2);
// Set the euid/egid back to root.
ASSERT_EQ(0, setegid(0));
ASSERT_EQ(0, seteuid(0));
}
TEST_F(ServiceFrameworkTest, PollIn) {
// Create a test service and add it to the dispatcher.
auto service = TestService::Create(kTestService1);
ASSERT_NE(nullptr, service);
ASSERT_EQ(0, dispatcher_->AddService(service));
// Create a client to service.
auto client = TestClient::Create(kTestService1);
ASSERT_NE(nullptr, client);
pollfd pfd{client->event_fd(), POLLIN, 0};
int count = poll(&pfd, 1, 0);
ASSERT_EQ(0, count);
client->SendPollInEvent();
count = poll(&pfd, 1, 10000 /*10s*/);
ASSERT_EQ(1, count);
ASSERT_TRUE((POLLIN & pfd.revents) != 0);
}
TEST_F(ServiceFrameworkTest, PollHup) {
// Create a test service and add it to the dispatcher.
auto service = TestService::Create(kTestService1);
ASSERT_NE(nullptr, service);
ASSERT_EQ(0, dispatcher_->AddService(service));
// Create a client to service.
auto client = TestClient::Create(kTestService1);
ASSERT_NE(nullptr, client);
pollfd pfd{client->event_fd(), POLLIN, 0};
int count = poll(&pfd, 1, 0);
ASSERT_EQ(0, count);
client->SendPollHupEvent();
count = poll(&pfd, 1, 10000 /*10s*/);
ASSERT_EQ(1, count);
auto event_status = client->GetEventMask(pfd.revents);
ASSERT_TRUE(event_status.ok());
ASSERT_TRUE((EPOLLHUP & event_status.get()) != 0);
}
TEST_F(ServiceFrameworkTest, LargeDataSum) {
// Create a test service and add it to the dispatcher.
auto service = TestService::Create(kTestService1);
ASSERT_NE(nullptr, service);
ASSERT_EQ(0, dispatcher_->AddService(service));
// Create a client to service.
auto client = TestClient::Create(kTestService1);
ASSERT_NE(nullptr, client);
std::array<int, kLargeDataSize> data_array;
std::iota(data_array.begin(), data_array.end(), 0);
int expected_sum = std::accumulate(data_array.begin(), data_array.end(), 0);
int sum = client->SendLargeDataReturnSum(data_array);
ASSERT_EQ(expected_sum, sum);
}
TEST_F(ServiceFrameworkTest, Cancel) {
// Create a test service and add it to the dispatcher.
auto service = TestService::Create(kTestService1, nullptr, true);
ASSERT_NE(nullptr, service);
ASSERT_EQ(0, dispatcher_->AddService(service));
// Create a client to service.
auto client = TestClient::Create(kTestService1);
ASSERT_NE(nullptr, client);
auto previous_time = std::chrono::system_clock::now();
dispatcher_->ReceiveAndDispatch(100); // 0.1 seconds should block.
auto time = std::chrono::system_clock::now();
ASSERT_LE(100, std::chrono::duration_cast<std::chrono::milliseconds>(
time - previous_time)
.count());
service->Cancel();
// Non-blocking. Return immediately.
dispatcher_->ReceiveAndDispatch(-1);
dispatcher_->ReceiveAndDispatch(-1);
}