blob: d8584b9f3b7d2d40351a67ffd0c99af22081189f [file] [log] [blame]
// Copyright 2015 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.
// This is really a unit test for |MasterConnectionManager| and
// |SlaveConnectionManager| (since they need to be tested together).
#include "mojo/edk/system/connection_manager.h"
#include <stdint.h>
#include <string>
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/threading/thread_checker.h"
#include "mojo/edk/embedder/master_process_delegate.h"
#include "mojo/edk/embedder/platform_channel_pair.h"
#include "mojo/edk/embedder/simple_platform_support.h"
#include "mojo/edk/embedder/slave_process_delegate.h"
#include "mojo/edk/system/master_connection_manager.h"
#include "mojo/edk/system/slave_connection_manager.h"
#include "mojo/edk/test/test_utils.h"
#include "mojo/public/cpp/system/macros.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace system {
namespace {
bool ArePlatformHandlesConnected(const embedder::PlatformHandle& h1,
const embedder::PlatformHandle& h2) {
const uint32_t w1 = 0xdeadbeef;
size_t num_bytes = 0;
if (!mojo::test::BlockingWrite(h1, &w1, sizeof(w1), &num_bytes) ||
num_bytes != sizeof(w1))
return false;
uint32_t r = 0;
num_bytes = 0;
if (!mojo::test::BlockingRead(h2, &r, sizeof(r), &num_bytes) ||
num_bytes != sizeof(r))
return false;
if (r != w1)
return false;
const uint32_t w2 = 0xfeedface;
num_bytes = 0;
if (!mojo::test::BlockingWrite(h1, &w2, sizeof(w2), &num_bytes) ||
num_bytes != sizeof(w2))
return false;
r = 0;
num_bytes = 0;
if (!mojo::test::BlockingRead(h2, &r, sizeof(r), &num_bytes) ||
num_bytes != sizeof(r))
return false;
if (r != w2)
return false;
return true;
}
bool IsValidSlaveProcessIdentifier(ProcessIdentifier process_identifier) {
return process_identifier != kInvalidProcessIdentifier &&
process_identifier != kMasterProcessIdentifier;
}
class TestSlaveInfo {
public:
explicit TestSlaveInfo(const std::string& name) : name_(name) {}
~TestSlaveInfo() { CHECK(thread_checker_.CalledOnValidThread()); }
const std::string& name() const { return name_; }
private:
base::ThreadChecker thread_checker_;
std::string name_;
MOJO_DISALLOW_COPY_AND_ASSIGN(TestSlaveInfo);
};
// Connects the given |slave| (with the given |slave_process_delegate|) to the
// given master, creating and using a |TestSlaveInfo| with the given
// |slave_name|, and returns the process identifier for the slave.
ProcessIdentifier ConnectSlave(
MasterConnectionManager* master,
embedder::SlaveProcessDelegate* slave_process_delegate,
SlaveConnectionManager* slave,
const std::string& slave_name) {
embedder::PlatformChannelPair platform_channel_pair;
ProcessIdentifier slave_process_identifier = master->AddSlave(
new TestSlaveInfo(slave_name), platform_channel_pair.PassServerHandle());
slave->Init(base::MessageLoop::current()->task_runner(),
slave_process_delegate, platform_channel_pair.PassClientHandle());
return slave_process_identifier;
}
class MockMasterProcessDelegate : public embedder::MasterProcessDelegate {
public:
MockMasterProcessDelegate()
: current_run_loop_(), on_slave_disconnect_calls_(0) {}
~MockMasterProcessDelegate() override {}
void RunUntilNotified() {
CHECK(!current_run_loop_);
base::RunLoop run_loop;
current_run_loop_ = &run_loop;
run_loop.Run();
current_run_loop_ = nullptr;
}
unsigned on_slave_disconnect_calls() const {
return on_slave_disconnect_calls_;
}
const std::string& last_slave_disconnect_name() const {
return last_slave_disconnect_name_;
}
// |embedder::MasterProcessDelegate| implementation:
void OnShutdownComplete() override { NOTREACHED(); }
void OnSlaveDisconnect(embedder::SlaveInfo slave_info) override {
CHECK(thread_checker_.CalledOnValidThread());
on_slave_disconnect_calls_++;
last_slave_disconnect_name_ =
static_cast<TestSlaveInfo*>(slave_info)->name();
DVLOG(1) << "Disconnected from slave process "
<< last_slave_disconnect_name_;
delete static_cast<TestSlaveInfo*>(slave_info);
if (current_run_loop_)
current_run_loop_->Quit();
}
private:
base::ThreadChecker thread_checker_;
base::RunLoop* current_run_loop_;
unsigned on_slave_disconnect_calls_;
std::string last_slave_disconnect_name_;
MOJO_DISALLOW_COPY_AND_ASSIGN(MockMasterProcessDelegate);
};
class MockSlaveProcessDelegate : public embedder::SlaveProcessDelegate {
public:
MockSlaveProcessDelegate()
: current_run_loop_(), on_master_disconnect_calls_(0) {}
~MockSlaveProcessDelegate() override {}
void RunUntilNotified() {
CHECK(!current_run_loop_);
base::RunLoop run_loop;
current_run_loop_ = &run_loop;
run_loop.Run();
current_run_loop_ = nullptr;
}
unsigned on_master_disconnect_calls() const {
return on_master_disconnect_calls_;
}
// |embedder::SlaveProcessDelegate| implementation:
void OnShutdownComplete() override { NOTREACHED(); }
void OnMasterDisconnect() override {
CHECK(thread_checker_.CalledOnValidThread());
on_master_disconnect_calls_++;
DVLOG(1) << "Disconnected from master process";
if (current_run_loop_)
current_run_loop_->Quit();
}
private:
base::ThreadChecker thread_checker_;
base::RunLoop* current_run_loop_;
unsigned on_master_disconnect_calls_;
MOJO_DISALLOW_COPY_AND_ASSIGN(MockSlaveProcessDelegate);
};
class ConnectionManagerTest : public testing::Test {
protected:
ConnectionManagerTest() {}
~ConnectionManagerTest() override {}
embedder::PlatformSupport* platform_support() { return &platform_support_; }
base::MessageLoop& message_loop() { return message_loop_; }
MockMasterProcessDelegate& master_process_delegate() {
return master_process_delegate_;
}
private:
embedder::SimplePlatformSupport platform_support_;
base::MessageLoop message_loop_;
MockMasterProcessDelegate master_process_delegate_;
MOJO_DISALLOW_COPY_AND_ASSIGN(ConnectionManagerTest);
};
TEST_F(ConnectionManagerTest, BasicConnectSlaves) {
MasterConnectionManager master(platform_support());
master.Init(base::MessageLoop::current()->task_runner(),
&master_process_delegate());
MockSlaveProcessDelegate slave1_process_delegate;
SlaveConnectionManager slave1(platform_support());
ProcessIdentifier slave1_id =
ConnectSlave(&master, &slave1_process_delegate, &slave1, "slave1");
EXPECT_TRUE(IsValidSlaveProcessIdentifier(slave1_id));
MockSlaveProcessDelegate slave2_process_delegate;
SlaveConnectionManager slave2(platform_support());
ProcessIdentifier slave2_id =
ConnectSlave(&master, &slave2_process_delegate, &slave2, "slave2");
EXPECT_TRUE(IsValidSlaveProcessIdentifier(slave2_id));
// TODO(vtl): If/when I add the ability to get one's own process identifier,
// there'll be more we can check.
EXPECT_NE(slave1_id, slave2_id);
ConnectionIdentifier connection_id = master.GenerateConnectionIdentifier();
EXPECT_TRUE(slave1.AllowConnect(connection_id));
EXPECT_TRUE(slave2.AllowConnect(connection_id));
ProcessIdentifier peer1 = kInvalidProcessIdentifier;
bool is_first = false;
embedder::ScopedPlatformHandle h1;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_NEW_CONNECTION,
slave1.Connect(connection_id, &peer1, &is_first, &h1));
EXPECT_EQ(slave2_id, peer1);
EXPECT_TRUE(is_first);
EXPECT_TRUE(h1.is_valid());
ProcessIdentifier peer2 = kInvalidProcessIdentifier;
embedder::ScopedPlatformHandle h2;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_NEW_CONNECTION,
slave2.Connect(connection_id, &peer2, &is_first, &h2));
EXPECT_EQ(slave1_id, peer2);
EXPECT_FALSE(is_first);
EXPECT_TRUE(h2.is_valid());
EXPECT_TRUE(ArePlatformHandlesConnected(h1.get(), h2.get()));
// The process manager shouldn't have gotten any notifications yet. (Spin the
// message loop to make sure none were enqueued.)
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, master_process_delegate().on_slave_disconnect_calls());
slave1.Shutdown();
// |OnSlaveDisconnect()| should be called once.
master_process_delegate().RunUntilNotified();
EXPECT_EQ(1u, master_process_delegate().on_slave_disconnect_calls());
EXPECT_EQ("slave1", master_process_delegate().last_slave_disconnect_name());
slave2.Shutdown();
// |OnSlaveDisconnect()| should be called again.
master_process_delegate().RunUntilNotified();
EXPECT_EQ(2u, master_process_delegate().on_slave_disconnect_calls());
EXPECT_EQ("slave2", master_process_delegate().last_slave_disconnect_name());
master.Shutdown();
// None of the above should result in |OnMasterDisconnect()| being called.
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, slave1_process_delegate.on_master_disconnect_calls());
EXPECT_EQ(0u, slave2_process_delegate.on_master_disconnect_calls());
}
TEST_F(ConnectionManagerTest, ShutdownMasterBeforeSlave) {
MasterConnectionManager master(platform_support());
master.Init(base::MessageLoop::current()->task_runner(),
&master_process_delegate());
MockSlaveProcessDelegate slave_process_delegate;
SlaveConnectionManager slave(platform_support());
ProcessIdentifier slave_id =
ConnectSlave(&master, &slave_process_delegate, &slave, "slave");
EXPECT_TRUE(IsValidSlaveProcessIdentifier(slave_id));
// The process manager shouldn't have gotten any notifications yet. (Spin the
// message loop to make sure none were enqueued.)
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, master_process_delegate().on_slave_disconnect_calls());
master.Shutdown();
// |OnSlaveDisconnect()| should be called.
master_process_delegate().RunUntilNotified();
EXPECT_EQ(1u, master_process_delegate().on_slave_disconnect_calls());
EXPECT_EQ("slave", master_process_delegate().last_slave_disconnect_name());
// |OnMasterDisconnect()| should also be (or have been) called.
slave_process_delegate.RunUntilNotified();
EXPECT_EQ(1u, slave_process_delegate.on_master_disconnect_calls());
slave.Shutdown();
}
TEST_F(ConnectionManagerTest, SlaveCancelConnect) {
MasterConnectionManager master(platform_support());
master.Init(base::MessageLoop::current()->task_runner(),
&master_process_delegate());
MockSlaveProcessDelegate slave1_process_delegate;
SlaveConnectionManager slave1(platform_support());
ProcessIdentifier slave1_id =
ConnectSlave(&master, &slave1_process_delegate, &slave1, "slave1");
EXPECT_TRUE(IsValidSlaveProcessIdentifier(slave1_id));
MockSlaveProcessDelegate slave2_process_delegate;
SlaveConnectionManager slave2(platform_support());
ProcessIdentifier slave2_id =
ConnectSlave(&master, &slave2_process_delegate, &slave2, "slave2");
EXPECT_TRUE(IsValidSlaveProcessIdentifier(slave2_id));
EXPECT_NE(slave1_id, slave2_id);
ConnectionIdentifier connection_id = master.GenerateConnectionIdentifier();
EXPECT_TRUE(slave1.AllowConnect(connection_id));
EXPECT_TRUE(slave2.AllowConnect(connection_id));
EXPECT_TRUE(slave1.CancelConnect(connection_id));
ProcessIdentifier peer2 = kInvalidProcessIdentifier;
bool is_first = false;
embedder::ScopedPlatformHandle h2;
EXPECT_EQ(ConnectionManager::Result::FAILURE,
slave2.Connect(connection_id, &peer2, &is_first, &h2));
EXPECT_EQ(kInvalidProcessIdentifier, peer2);
EXPECT_FALSE(is_first);
EXPECT_FALSE(h2.is_valid());
slave1.Shutdown();
slave2.Shutdown();
master.Shutdown();
}
// Tests that pending connections are removed on error.
TEST_F(ConnectionManagerTest, ErrorRemovePending) {
MasterConnectionManager master(platform_support());
master.Init(base::MessageLoop::current()->task_runner(),
&master_process_delegate());
MockSlaveProcessDelegate slave1_process_delegate;
SlaveConnectionManager slave1(platform_support());
ProcessIdentifier slave1_id =
ConnectSlave(&master, &slave1_process_delegate, &slave1, "slave1");
EXPECT_TRUE(IsValidSlaveProcessIdentifier(slave1_id));
MockSlaveProcessDelegate slave2_process_delegate;
SlaveConnectionManager slave2(platform_support());
ProcessIdentifier slave2_id =
ConnectSlave(&master, &slave2_process_delegate, &slave2, "slave2");
EXPECT_TRUE(IsValidSlaveProcessIdentifier(slave2_id));
EXPECT_NE(slave1_id, slave2_id);
ConnectionIdentifier connection_id = master.GenerateConnectionIdentifier();
EXPECT_TRUE(slave1.AllowConnect(connection_id));
EXPECT_TRUE(slave2.AllowConnect(connection_id));
slave1.Shutdown();
// |OnSlaveDisconnect()| should be called. After it's called, this means that
// the disconnect has been detected and handled, including the removal of the
// pending connection.
master_process_delegate().RunUntilNotified();
EXPECT_EQ(1u, master_process_delegate().on_slave_disconnect_calls());
ProcessIdentifier peer2 = kInvalidProcessIdentifier;
bool is_first = false;
embedder::ScopedPlatformHandle h2;
EXPECT_EQ(ConnectionManager::Result::FAILURE,
slave2.Connect(connection_id, &peer2, &is_first, &h2));
EXPECT_EQ(kInvalidProcessIdentifier, peer2);
EXPECT_FALSE(is_first);
EXPECT_FALSE(h2.is_valid());
slave2.Shutdown();
master.Shutdown();
}
TEST_F(ConnectionManagerTest, ConnectSlaveToSelf) {
MasterConnectionManager master(platform_support());
master.Init(base::MessageLoop::current()->task_runner(),
&master_process_delegate());
MockSlaveProcessDelegate slave_process_delegate;
SlaveConnectionManager slave(platform_support());
ProcessIdentifier slave_id =
ConnectSlave(&master, &slave_process_delegate, &slave, "slave");
EXPECT_TRUE(IsValidSlaveProcessIdentifier(slave_id));
ConnectionIdentifier connection_id = master.GenerateConnectionIdentifier();
EXPECT_TRUE(slave.AllowConnect(connection_id));
EXPECT_TRUE(slave.AllowConnect(connection_id));
ProcessIdentifier peer1 = kInvalidProcessIdentifier;
bool is_first = false;
embedder::ScopedPlatformHandle h1;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_SAME_PROCESS,
slave.Connect(connection_id, &peer1, &is_first, &h1));
EXPECT_EQ(slave_id, peer1);
EXPECT_TRUE(is_first);
EXPECT_FALSE(h1.is_valid());
ProcessIdentifier peer2 = kInvalidProcessIdentifier;
embedder::ScopedPlatformHandle h2;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_SAME_PROCESS,
slave.Connect(connection_id, &peer2, &is_first, &h2));
EXPECT_EQ(slave_id, peer2);
EXPECT_FALSE(is_first);
EXPECT_FALSE(h2.is_valid());
slave.Shutdown();
master.Shutdown();
}
TEST_F(ConnectionManagerTest, ConnectSlavesTwice) {
MasterConnectionManager master(platform_support());
master.Init(base::MessageLoop::current()->task_runner(),
&master_process_delegate());
MockSlaveProcessDelegate slave1_process_delegate;
SlaveConnectionManager slave1(platform_support());
ProcessIdentifier slave1_id =
ConnectSlave(&master, &slave1_process_delegate, &slave1, "slave1");
EXPECT_TRUE(IsValidSlaveProcessIdentifier(slave1_id));
MockSlaveProcessDelegate slave2_process_delegate;
SlaveConnectionManager slave2(platform_support());
ProcessIdentifier slave2_id =
ConnectSlave(&master, &slave2_process_delegate, &slave2, "slave2");
EXPECT_TRUE(IsValidSlaveProcessIdentifier(slave2_id));
EXPECT_NE(slave1_id, slave2_id);
ConnectionIdentifier connection_id = master.GenerateConnectionIdentifier();
EXPECT_TRUE(slave1.AllowConnect(connection_id));
EXPECT_TRUE(slave2.AllowConnect(connection_id));
ProcessIdentifier peer1 = kInvalidProcessIdentifier;
bool is_first = false;
embedder::ScopedPlatformHandle h1;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_NEW_CONNECTION,
slave1.Connect(connection_id, &peer1, &is_first, &h1));
EXPECT_EQ(slave2_id, peer1);
EXPECT_TRUE(is_first);
ProcessIdentifier peer2 = kInvalidProcessIdentifier;
embedder::ScopedPlatformHandle h2;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_NEW_CONNECTION,
slave2.Connect(connection_id, &peer2, &is_first, &h2));
EXPECT_EQ(slave1_id, peer2);
EXPECT_FALSE(is_first);
EXPECT_TRUE(ArePlatformHandlesConnected(h1.get(), h2.get()));
// TODO(vtl): Currently, the master doesn't detect the case of connecting a
// pair of slaves that are already connected. (Doing so would require more
// careful tracking and is prone to races -- especially if we want slaves to
// be able to tear down no-longer-needed connections.) But the slaves should
// be able to do the tracking themselves (using the peer process identifiers).
connection_id = master.GenerateConnectionIdentifier();
EXPECT_TRUE(slave1.AllowConnect(connection_id));
EXPECT_TRUE(slave2.AllowConnect(connection_id));
h1.reset();
h2.reset();
ProcessIdentifier second_peer2 = kInvalidProcessIdentifier;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_REUSE_CONNECTION,
slave2.Connect(connection_id, &second_peer2, &is_first, &h2));
EXPECT_EQ(peer2, second_peer2);
EXPECT_TRUE(is_first);
EXPECT_FALSE(h2.is_valid());
ProcessIdentifier second_peer1 = kInvalidProcessIdentifier;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_REUSE_CONNECTION,
slave1.Connect(connection_id, &second_peer1, &is_first, &h1));
EXPECT_EQ(peer1, second_peer1);
EXPECT_FALSE(is_first);
EXPECT_FALSE(h1.is_valid());
slave2.Shutdown();
slave1.Shutdown();
master.Shutdown();
}
TEST_F(ConnectionManagerTest, OverlappingSlaveConnects) {
MasterConnectionManager master(platform_support());
master.Init(base::MessageLoop::current()->task_runner(),
&master_process_delegate());
MockSlaveProcessDelegate slave1_process_delegate;
SlaveConnectionManager slave1(platform_support());
ProcessIdentifier slave1_id =
ConnectSlave(&master, &slave1_process_delegate, &slave1, "slave1");
EXPECT_TRUE(IsValidSlaveProcessIdentifier(slave1_id));
MockSlaveProcessDelegate slave2_process_delegate;
SlaveConnectionManager slave2(platform_support());
ProcessIdentifier slave2_id =
ConnectSlave(&master, &slave2_process_delegate, &slave2, "slave2");
EXPECT_TRUE(IsValidSlaveProcessIdentifier(slave2_id));
EXPECT_NE(slave1_id, slave2_id);
ConnectionIdentifier connection_id1 = master.GenerateConnectionIdentifier();
EXPECT_TRUE(slave1.AllowConnect(connection_id1));
EXPECT_TRUE(slave2.AllowConnect(connection_id1));
ConnectionIdentifier connection_id2 = master.GenerateConnectionIdentifier();
EXPECT_TRUE(slave1.AllowConnect(connection_id2));
EXPECT_TRUE(slave2.AllowConnect(connection_id2));
ProcessIdentifier peer1 = kInvalidProcessIdentifier;
bool is_first = false;
embedder::ScopedPlatformHandle h1;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_NEW_CONNECTION,
slave1.Connect(connection_id1, &peer1, &is_first, &h1));
EXPECT_EQ(slave2_id, peer1);
EXPECT_TRUE(is_first);
ProcessIdentifier peer2 = kInvalidProcessIdentifier;
embedder::ScopedPlatformHandle h2;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_NEW_CONNECTION,
slave2.Connect(connection_id2, &peer2, &is_first, &h2));
EXPECT_EQ(slave1_id, peer2);
EXPECT_TRUE(is_first);
EXPECT_TRUE(ArePlatformHandlesConnected(h1.get(), h2.get()));
h1.reset();
h2.reset();
ProcessIdentifier second_peer1 = kInvalidProcessIdentifier;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_REUSE_CONNECTION,
slave1.Connect(connection_id2, &second_peer1, &is_first, &h1));
EXPECT_EQ(peer1, second_peer1);
EXPECT_FALSE(is_first);
EXPECT_FALSE(h1.is_valid());
ProcessIdentifier second_peer2 = kInvalidProcessIdentifier;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_REUSE_CONNECTION,
slave2.Connect(connection_id1, &second_peer2, &is_first, &h2));
EXPECT_EQ(peer2, second_peer2);
EXPECT_FALSE(is_first);
EXPECT_FALSE(h2.is_valid());
slave2.Shutdown();
slave1.Shutdown();
master.Shutdown();
}
TEST_F(ConnectionManagerTest, ConnectMasterToSlave) {
MasterConnectionManager master(platform_support());
master.Init(base::MessageLoop::current()->task_runner(),
&master_process_delegate());
MockSlaveProcessDelegate slave_process_delegate;
SlaveConnectionManager slave(platform_support());
ProcessIdentifier slave_id =
ConnectSlave(&master, &slave_process_delegate, &slave, "slave");
EXPECT_TRUE(IsValidSlaveProcessIdentifier(slave_id));
ConnectionIdentifier connection_id = master.GenerateConnectionIdentifier();
EXPECT_TRUE(master.AllowConnect(connection_id));
EXPECT_TRUE(slave.AllowConnect(connection_id));
ProcessIdentifier master_peer = kInvalidProcessIdentifier;
bool is_first = false;
embedder::ScopedPlatformHandle master_h;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_NEW_CONNECTION,
master.Connect(connection_id, &master_peer, &is_first, &master_h));
EXPECT_EQ(slave_id, master_peer);
EXPECT_TRUE(is_first);
EXPECT_TRUE(master_h.is_valid());
ProcessIdentifier slave_peer = kInvalidProcessIdentifier;
embedder::ScopedPlatformHandle slave_h;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_NEW_CONNECTION,
slave.Connect(connection_id, &slave_peer, &is_first, &slave_h));
EXPECT_EQ(kMasterProcessIdentifier, slave_peer);
EXPECT_FALSE(is_first);
EXPECT_TRUE(slave_h.is_valid());
EXPECT_TRUE(ArePlatformHandlesConnected(master_h.get(), slave_h.get()));
slave.Shutdown();
master.Shutdown();
}
TEST_F(ConnectionManagerTest, ConnectMasterToSelf) {
MasterConnectionManager master(platform_support());
master.Init(base::MessageLoop::current()->task_runner(),
&master_process_delegate());
ConnectionIdentifier connection_id = master.GenerateConnectionIdentifier();
EXPECT_TRUE(master.AllowConnect(connection_id));
EXPECT_TRUE(master.AllowConnect(connection_id));
ProcessIdentifier peer1 = kInvalidProcessIdentifier;
bool is_first = false;
embedder::ScopedPlatformHandle h1;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_SAME_PROCESS,
master.Connect(connection_id, &peer1, &is_first, &h1));
EXPECT_EQ(kMasterProcessIdentifier, peer1);
EXPECT_TRUE(is_first);
EXPECT_FALSE(h1.is_valid());
ProcessIdentifier peer2 = kInvalidProcessIdentifier;
embedder::ScopedPlatformHandle h2;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_SAME_PROCESS,
master.Connect(connection_id, &peer2, &is_first, &h2));
EXPECT_EQ(kMasterProcessIdentifier, peer2);
EXPECT_FALSE(is_first);
EXPECT_FALSE(h2.is_valid());
EXPECT_EQ(peer1, peer2);
master.Shutdown();
}
TEST_F(ConnectionManagerTest, MasterCancelConnect) {
MasterConnectionManager master(platform_support());
master.Init(base::MessageLoop::current()->task_runner(),
&master_process_delegate());
MockSlaveProcessDelegate slave_process_delegate;
SlaveConnectionManager slave(platform_support());
ProcessIdentifier slave_id =
ConnectSlave(&master, &slave_process_delegate, &slave, "slave");
EXPECT_TRUE(IsValidSlaveProcessIdentifier(slave_id));
ConnectionIdentifier connection_id = master.GenerateConnectionIdentifier();
EXPECT_TRUE(master.AllowConnect(connection_id));
EXPECT_TRUE(slave.AllowConnect(connection_id));
EXPECT_TRUE(master.CancelConnect(connection_id));
ProcessIdentifier peer = kInvalidProcessIdentifier;
bool is_first = false;
embedder::ScopedPlatformHandle h;
EXPECT_EQ(ConnectionManager::Result::FAILURE,
slave.Connect(connection_id, &peer, &is_first, &h));
EXPECT_EQ(kInvalidProcessIdentifier, peer);
EXPECT_FALSE(is_first);
EXPECT_FALSE(h.is_valid());
slave.Shutdown();
master.Shutdown();
}
TEST_F(ConnectionManagerTest, AddSlaveThenImmediateShutdown) {
MasterConnectionManager master(platform_support());
master.Init(base::MessageLoop::current()->task_runner(),
&master_process_delegate());
MockSlaveProcessDelegate slave_process_delegate;
SlaveConnectionManager slave(platform_support());
embedder::PlatformChannelPair platform_channel_pair;
ProcessIdentifier slave_id = master.AddSlave(
new TestSlaveInfo("slave"), platform_channel_pair.PassServerHandle());
master.Shutdown();
EXPECT_TRUE(IsValidSlaveProcessIdentifier(slave_id));
// Since we never initialized |slave|, we don't have to shut it down.
}
TEST_F(ConnectionManagerTest, AddSlaveAndBootstrap) {
MasterConnectionManager master(platform_support());
master.Init(base::MessageLoop::current()->task_runner(),
&master_process_delegate());
embedder::PlatformChannelPair platform_channel_pair;
ConnectionIdentifier connection_id = master.GenerateConnectionIdentifier();
ProcessIdentifier slave_id = master.AddSlaveAndBootstrap(
new TestSlaveInfo("slave"), platform_channel_pair.PassServerHandle(),
connection_id);
EXPECT_TRUE(IsValidSlaveProcessIdentifier(slave_id));
embedder::ScopedPlatformHandle h1;
ProcessIdentifier master_peer = kInvalidProcessIdentifier;
bool is_first = false;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_NEW_CONNECTION,
master.Connect(connection_id, &master_peer, &is_first, &h1));
EXPECT_EQ(slave_id, master_peer);
EXPECT_TRUE(is_first);
EXPECT_TRUE(h1.is_valid());
// We can delay creating/initializing |slave| for quite a while.
MockSlaveProcessDelegate slave_process_delegate;
SlaveConnectionManager slave(platform_support());
slave.Init(base::MessageLoop::current()->task_runner(),
&slave_process_delegate, platform_channel_pair.PassClientHandle());
ProcessIdentifier slave_peer = kInvalidProcessIdentifier;
embedder::ScopedPlatformHandle h2;
EXPECT_EQ(ConnectionManager::Result::SUCCESS_CONNECT_NEW_CONNECTION,
slave.Connect(connection_id, &slave_peer, &is_first, &h2));
EXPECT_EQ(kMasterProcessIdentifier, slave_peer);
EXPECT_FALSE(is_first);
EXPECT_TRUE(ArePlatformHandlesConnected(h1.get(), h2.get()));
slave.Shutdown();
master.Shutdown();
}
// TODO(vtl): More shutdown cases for |AddSlaveAndBootstrap()|?
} // namespace
} // namespace system
} // namespace mojo