blob: 07218c69297d9401106e7579096574c1cc35dec4 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ipc/mojo/ipc_channel_mojo.h"
#include "base/base_paths.h"
#include "base/files/file.h"
#include "base/message_loop/message_loop.h"
#include "base/path_service.h"
#include "base/pickle.h"
#include "base/threading/thread.h"
#include "ipc/ipc_message.h"
#include "ipc/ipc_test_base.h"
#include "ipc/ipc_test_channel_listener.h"
#include "ipc/mojo/ipc_channel_mojo_host.h"
#include "ipc/mojo/ipc_channel_mojo_readers.h"
#if defined(OS_POSIX)
#include "base/file_descriptor_posix.h"
#endif
namespace {
class ListenerThatExpectsOK : public IPC::Listener {
public:
ListenerThatExpectsOK()
: received_ok_(false) {}
~ListenerThatExpectsOK() override {}
bool OnMessageReceived(const IPC::Message& message) override {
PickleIterator iter(message);
std::string should_be_ok;
EXPECT_TRUE(iter.ReadString(&should_be_ok));
EXPECT_EQ(should_be_ok, "OK");
received_ok_ = true;
base::MessageLoop::current()->Quit();
return true;
}
void OnChannelError() override {
// The connection should be healthy while the listener is waiting
// message. An error can occur after that because the peer
// process dies.
DCHECK(received_ok_);
}
static void SendOK(IPC::Sender* sender) {
IPC::Message* message = new IPC::Message(
0, 2, IPC::Message::PRIORITY_NORMAL);
message->WriteString(std::string("OK"));
ASSERT_TRUE(sender->Send(message));
}
private:
bool received_ok_;
};
class ChannelClient {
public:
explicit ChannelClient(IPC::Listener* listener, const char* name) {
channel_ = IPC::ChannelMojo::Create(NULL,
IPCTestBase::GetChannelName(name),
IPC::Channel::MODE_CLIENT,
listener);
}
void Connect() {
CHECK(channel_->Connect());
}
IPC::ChannelMojo* channel() const { return channel_.get(); }
private:
base::MessageLoopForIO main_message_loop_;
scoped_ptr<IPC::ChannelMojo> channel_;
};
class IPCChannelMojoTest : public IPCTestBase {
protected:
scoped_ptr<IPC::ChannelFactory> CreateChannelFactory(
const IPC::ChannelHandle& handle,
base::TaskRunner* runner) override {
host_.reset(new IPC::ChannelMojoHost(task_runner()));
return IPC::ChannelMojo::CreateServerFactory(host_->channel_delegate(),
handle);
}
bool DidStartClient() override {
bool ok = IPCTestBase::DidStartClient();
DCHECK(ok);
host_->OnClientLaunched(client_process());
return ok;
}
private:
scoped_ptr<IPC::ChannelMojoHost> host_;
};
class TestChannelListenerWithExtraExpectations
: public IPC::TestChannelListener {
public:
TestChannelListenerWithExtraExpectations()
: is_connected_called_(false) {
}
void OnChannelConnected(int32 peer_pid) override {
IPC::TestChannelListener::OnChannelConnected(peer_pid);
EXPECT_TRUE(base::kNullProcessId != peer_pid);
is_connected_called_ = true;
}
bool is_connected_called() const { return is_connected_called_; }
private:
bool is_connected_called_;
};
TEST_F(IPCChannelMojoTest, ConnectedFromClient) {
Init("IPCChannelMojoTestClient");
// Set up IPC channel and start client.
TestChannelListenerWithExtraExpectations listener;
CreateChannel(&listener);
listener.Init(sender());
ASSERT_TRUE(ConnectChannel());
ASSERT_TRUE(StartClient());
IPC::TestChannelListener::SendOneMessage(
sender(), "hello from parent");
base::MessageLoop::current()->Run();
EXPECT_TRUE(base::kNullProcessId != this->channel()->GetPeerPID());
this->channel()->Close();
EXPECT_TRUE(WaitForClientShutdown());
EXPECT_TRUE(listener.is_connected_called());
EXPECT_TRUE(listener.HasSentAll());
DestroyChannel();
}
// A long running process that connects to us
MULTIPROCESS_IPC_TEST_CLIENT_MAIN(IPCChannelMojoTestClient) {
TestChannelListenerWithExtraExpectations listener;
ChannelClient client(&listener, "IPCChannelMojoTestClient");
client.Connect();
listener.Init(client.channel());
IPC::TestChannelListener::SendOneMessage(
client.channel(), "hello from child");
base::MessageLoop::current()->Run();
EXPECT_TRUE(listener.is_connected_called());
EXPECT_TRUE(listener.HasSentAll());
return 0;
}
class ListenerExpectingErrors : public IPC::Listener {
public:
ListenerExpectingErrors()
: has_error_(false) {
}
void OnChannelConnected(int32 peer_pid) override {
base::MessageLoop::current()->Quit();
}
bool OnMessageReceived(const IPC::Message& message) override { return true; }
void OnChannelError() override {
has_error_ = true;
base::MessageLoop::current()->Quit();
}
bool has_error() const { return has_error_; }
private:
bool has_error_;
};
class IPCChannelMojoErrorTest : public IPCTestBase {
protected:
scoped_ptr<IPC::ChannelFactory> CreateChannelFactory(
const IPC::ChannelHandle& handle,
base::TaskRunner* runner) override {
host_.reset(new IPC::ChannelMojoHost(task_runner()));
return IPC::ChannelMojo::CreateServerFactory(host_->channel_delegate(),
handle);
}
bool DidStartClient() override {
bool ok = IPCTestBase::DidStartClient();
DCHECK(ok);
host_->OnClientLaunched(client_process());
return ok;
}
private:
scoped_ptr<IPC::ChannelMojoHost> host_;
};
class ListenerThatQuits : public IPC::Listener {
public:
ListenerThatQuits() {
}
bool OnMessageReceived(const IPC::Message& message) override {
return true;
}
void OnChannelConnected(int32 peer_pid) override {
base::MessageLoop::current()->Quit();
}
};
// A long running process that connects to us.
MULTIPROCESS_IPC_TEST_CLIENT_MAIN(IPCChannelMojoErraticTestClient) {
ListenerThatQuits listener;
ChannelClient client(&listener, "IPCChannelMojoErraticTestClient");
client.Connect();
base::MessageLoop::current()->Run();
return 0;
}
TEST_F(IPCChannelMojoErrorTest, SendFailWithPendingMessages) {
Init("IPCChannelMojoErraticTestClient");
// Set up IPC channel and start client.
ListenerExpectingErrors listener;
CreateChannel(&listener);
ASSERT_TRUE(ConnectChannel());
// This matches a value in mojo/edk/system/constants.h
const int kMaxMessageNumBytes = 4 * 1024 * 1024;
std::string overly_large_data(kMaxMessageNumBytes, '*');
// This messages are queued as pending.
for (size_t i = 0; i < 10; ++i) {
IPC::TestChannelListener::SendOneMessage(
sender(), overly_large_data.c_str());
}
ASSERT_TRUE(StartClient());
base::MessageLoop::current()->Run();
this->channel()->Close();
EXPECT_TRUE(WaitForClientShutdown());
EXPECT_TRUE(listener.has_error());
DestroyChannel();
}
#if defined(OS_WIN)
class IPCChannelMojoDeadHandleTest : public IPCTestBase {
protected:
virtual scoped_ptr<IPC::ChannelFactory> CreateChannelFactory(
const IPC::ChannelHandle& handle,
base::TaskRunner* runner) override {
host_.reset(new IPC::ChannelMojoHost(task_runner()));
return IPC::ChannelMojo::CreateServerFactory(host_->channel_delegate(),
handle);
}
virtual bool DidStartClient() override {
IPCTestBase::DidStartClient();
base::ProcessHandle client = client_process();
// Forces GetFileHandleForProcess() fail. It happens occasionally
// in production, so we should exercise it somehow.
::CloseHandle(client);
host_->OnClientLaunched(client);
return true;
}
private:
scoped_ptr<IPC::ChannelMojoHost> host_;
};
TEST_F(IPCChannelMojoDeadHandleTest, InvalidClientHandle) {
// Any client type is fine as it is going to be killed anyway.
Init("IPCChannelMojoTestDoNothingClient");
// Set up IPC channel and start client.
ListenerExpectingErrors listener;
CreateChannel(&listener);
ASSERT_TRUE(ConnectChannel());
ASSERT_TRUE(StartClient());
base::MessageLoop::current()->Run();
this->channel()->Close();
// WaitForClientShutdown() fails as client_hanadle() is already
// closed.
EXPECT_FALSE(WaitForClientShutdown());
EXPECT_TRUE(listener.has_error());
DestroyChannel();
}
MULTIPROCESS_IPC_TEST_CLIENT_MAIN(IPCChannelMojoTestDoNothingClient) {
ListenerThatQuits listener;
ChannelClient client(&listener, "IPCChannelMojoTestDoNothingClient");
client.Connect();
// Quits without running the message loop as this client won't
// receive any messages from the server.
return 0;
}
#endif
#if defined(OS_POSIX)
class ListenerThatExpectsFile : public IPC::Listener {
public:
ListenerThatExpectsFile()
: sender_(NULL) {}
~ListenerThatExpectsFile() override {}
bool OnMessageReceived(const IPC::Message& message) override {
PickleIterator iter(message);
base::ScopedFD fd;
EXPECT_TRUE(message.ReadFile(&iter, &fd));
base::File file(fd.release());
std::string content(GetSendingFileContent().size(), ' ');
file.Read(0, &content[0], content.size());
EXPECT_EQ(content, GetSendingFileContent());
base::MessageLoop::current()->Quit();
ListenerThatExpectsOK::SendOK(sender_);
return true;
}
void OnChannelError() override {
NOTREACHED();
}
static std::string GetSendingFileContent() {
return "Hello";
}
static base::FilePath GetSendingFilePath() {
base::FilePath path;
bool ok = PathService::Get(base::DIR_CACHE, &path);
EXPECT_TRUE(ok);
return path.Append("ListenerThatExpectsFile.txt");
}
static void WriteAndSendFile(IPC::Sender* sender, base::File& file) {
std::string content = GetSendingFileContent();
file.WriteAtCurrentPos(content.data(), content.size());
file.Flush();
IPC::Message* message = new IPC::Message(
0, 2, IPC::Message::PRIORITY_NORMAL);
message->WriteFile(base::ScopedFD(file.TakePlatformFile()));
ASSERT_TRUE(sender->Send(message));
}
void set_sender(IPC::Sender* sender) { sender_ = sender; }
private:
IPC::Sender* sender_;
};
TEST_F(IPCChannelMojoTest, SendPlatformHandle) {
Init("IPCChannelMojoTestSendPlatformHandleClient");
ListenerThatExpectsOK listener;
CreateChannel(&listener);
ASSERT_TRUE(ConnectChannel());
ASSERT_TRUE(StartClient());
base::File file(ListenerThatExpectsFile::GetSendingFilePath(),
base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE |
base::File::FLAG_READ);
ListenerThatExpectsFile::WriteAndSendFile(channel(), file);
base::MessageLoop::current()->Run();
this->channel()->Close();
EXPECT_TRUE(WaitForClientShutdown());
DestroyChannel();
}
MULTIPROCESS_IPC_TEST_CLIENT_MAIN(IPCChannelMojoTestSendPlatformHandleClient) {
ListenerThatExpectsFile listener;
ChannelClient client(
&listener, "IPCChannelMojoTestSendPlatformHandleClient");
client.Connect();
listener.set_sender(client.channel());
base::MessageLoop::current()->Run();
return 0;
}
#endif
} // namespace