blob: 31578370399034a094fc188bc1bbe165181e7e1c [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"
#if defined(OS_POSIX)
#include "base/file_descriptor_posix.h"
#endif
namespace {
class ListenerThatExpectsOK : public IPC::Listener {
public:
ListenerThatExpectsOK()
: received_ok_(false) {}
virtual ~ListenerThatExpectsOK() {}
virtual 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;
}
virtual 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 ListenerThatShouldBeNeverCalled : public IPC::Listener {
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
NOTREACHED();
return true;
}
virtual void OnChannelError() OVERRIDE {
NOTREACHED();
}
virtual void OnChannelConnected(int32 peer_pid) OVERRIDE {
NOTREACHED();
}
virtual void OnBadMessageReceived(const IPC::Message& message) OVERRIDE {
NOTREACHED();
}
};
class ChannelClient {
public:
explicit ChannelClient(IPC::Listener* listener, const char* name) {
scoped_ptr<IPC::Channel> bootstrap(IPC::Channel::CreateClient(
IPCTestBase::GetChannelName(name),
&never_called_));
channel_ = IPC::ChannelMojo::Create(
bootstrap.Pass(), IPC::Channel::MODE_CLIENT, listener,
main_message_loop_.message_loop_proxy());
}
void Connect() {
CHECK(channel_->Connect());
}
IPC::ChannelMojo* channel() const { return channel_.get(); }
private:
scoped_ptr<IPC::ChannelMojo> channel_;
ListenerThatShouldBeNeverCalled never_called_;
base::MessageLoopForIO main_message_loop_;
};
class IPCChannelMojoTest : public IPCTestBase {
public:
void CreateMojoChannel(IPC::Listener* listener);
protected:
virtual void SetUp() OVERRIDE {
IPCTestBase::SetUp();
}
ListenerThatShouldBeNeverCalled never_called_;
};
void IPCChannelMojoTest::CreateMojoChannel(IPC::Listener* listener) {
CreateChannel(&never_called_);
scoped_ptr<IPC::Channel> mojo_channel = IPC::ChannelMojo::Create(
ReleaseChannel(), IPC::Channel::MODE_SERVER, listener,
io_thread_task_runner()).PassAs<IPC::Channel>();
SetChannel(mojo_channel.PassAs<IPC::Channel>());
}
class TestChannelListenerWithExtraExpectations
: public IPC::TestChannelListener {
public:
TestChannelListenerWithExtraExpectations()
: is_connected_called_(false) {
}
virtual 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;
CreateMojoChannel(&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;
}
#if defined(OS_POSIX)
class ListenerThatExpectsFile : public IPC::Listener {
public:
ListenerThatExpectsFile()
: sender_(NULL) {}
virtual ~ListenerThatExpectsFile() {}
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE {
PickleIterator iter(message);
base::FileDescriptor desc;
EXPECT_TRUE(message.ReadFileDescriptor(&iter, &desc));
std::string content(GetSendingFileContent().size(), ' ');
base::File file(desc.fd);
file.Read(0, &content[0], content.size());
EXPECT_EQ(content, GetSendingFileContent());
base::MessageLoop::current()->Quit();
ListenerThatExpectsOK::SendOK(sender_);
return true;
}
virtual 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->WriteFileDescriptor(
base::FileDescriptor(file.TakePlatformFile(), false));
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;
CreateMojoChannel(&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