|  | // 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 |