Index: ipc/ipc_channel_mojo_unittest.cc |
diff --git a/ipc/ipc_channel_mojo_unittest.cc b/ipc/ipc_channel_mojo_unittest.cc |
index d918d9e3d080a4816fc2272e14604e5b3bd13c76..07b8fe1dbb2f8a022c173ab1257939360511106e 100644 |
--- a/ipc/ipc_channel_mojo_unittest.cc |
+++ b/ipc/ipc_channel_mojo_unittest.cc |
@@ -739,7 +739,7 @@ class ChannelProxyRunner { |
listener, io_thread_.task_runner(), &never_signaled_); |
} |
- void RunProxy() { |
+ void RunProxy(bool create_paused) { |
std::unique_ptr<IPC::ChannelFactory> factory; |
if (for_server_) { |
factory = IPC::ChannelMojo::CreateServerFactory( |
@@ -748,7 +748,7 @@ class ChannelProxyRunner { |
factory = IPC::ChannelMojo::CreateClientFactory( |
std::move(handle_), io_thread_.task_runner()); |
} |
- proxy_->Init(std::move(factory), true); |
+ proxy_->Init(std::move(factory), true, create_paused); |
} |
IPC::ChannelProxy* proxy() { return proxy_.get(); } |
@@ -771,7 +771,9 @@ class IPCChannelProxyMojoTest : public IPCChannelMojoTestBase { |
runner_.reset(new ChannelProxyRunner(TakeHandle(), true)); |
} |
void CreateProxy(IPC::Listener* listener) { runner_->CreateProxy(listener); } |
- void RunProxy() { runner_->RunProxy(); } |
+ void RunProxy(bool create_paused = false) { |
+ runner_->RunProxy(create_paused); |
+ } |
void DestroyProxy() { |
runner_.reset(); |
base::RunLoop().RunUntilIdle(); |
@@ -876,7 +878,7 @@ class ChannelProxyClient { |
void CreateProxy(IPC::Listener* listener) { runner_->CreateProxy(listener); } |
- void RunProxy() { runner_->RunProxy(); } |
+ void RunProxy() { runner_->RunProxy(false); } |
void DestroyProxy() { |
runner_.reset(); |
@@ -923,6 +925,97 @@ DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT(ProxyThreadAssociatedInterfaceClient, |
DestroyProxy(); |
} |
+class ListenerWithIndirectProxyAssociatedInterface |
+ : public IPC::Listener, |
+ public IPC::mojom::IndirectTestDriver, |
+ public IPC::mojom::PingReceiver { |
+ public: |
+ ListenerWithIndirectProxyAssociatedInterface() |
+ : driver_binding_(this), ping_receiver_binding_(this) {} |
+ ~ListenerWithIndirectProxyAssociatedInterface() override {} |
+ |
+ bool OnMessageReceived(const IPC::Message& message) override { return true; } |
+ |
+ void RegisterInterfaceFactory(IPC::ChannelProxy* proxy) { |
+ proxy->AddAssociatedInterface( |
+ base::Bind(&ListenerWithIndirectProxyAssociatedInterface::BindRequest, |
+ base::Unretained(this))); |
+ } |
+ |
+ void set_ping_handler(const base::Closure& handler) { |
+ ping_handler_ = handler; |
+ } |
+ |
+ private: |
+ // IPC::mojom::IndirectTestDriver: |
+ void GetPingReceiver( |
+ IPC::mojom::PingReceiverAssociatedRequest request) override { |
+ ping_receiver_binding_.Bind(std::move(request)); |
+ } |
+ |
+ // IPC::mojom::PingReceiver: |
+ void Ping(const PingCallback& callback) override { |
+ callback.Run(); |
+ ping_handler_.Run(); |
+ } |
+ |
+ void BindRequest(IPC::mojom::IndirectTestDriverAssociatedRequest request) { |
+ DCHECK(!driver_binding_.is_bound()); |
+ driver_binding_.Bind(std::move(request)); |
+ } |
+ |
+ mojo::AssociatedBinding<IPC::mojom::IndirectTestDriver> driver_binding_; |
+ mojo::AssociatedBinding<IPC::mojom::PingReceiver> ping_receiver_binding_; |
+ |
+ base::Closure ping_handler_; |
+}; |
+ |
+TEST_F(IPCChannelProxyMojoTest, ProxyThreadAssociatedInterfaceIndirect) { |
+ // Tests that we can pipeline interface requests and subsequent messages |
+ // targeting proxy thread bindings, and the channel will still dispatch |
+ // messages appropriately. |
+ |
+ InitWithMojo("ProxyThreadAssociatedInterfaceIndirectClient"); |
+ |
+ ListenerWithIndirectProxyAssociatedInterface listener; |
+ CreateProxy(&listener); |
+ listener.RegisterInterfaceFactory(proxy()); |
+ RunProxy(); |
+ |
+ base::RunLoop loop; |
+ listener.set_ping_handler(loop.QuitClosure()); |
+ loop.Run(); |
+ |
+ EXPECT_TRUE(WaitForClientShutdown()); |
+ |
+ DestroyProxy(); |
+} |
+ |
+DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT( |
+ ProxyThreadAssociatedInterfaceIndirectClient, |
+ ChannelProxyClient) { |
+ DummyListener listener; |
+ CreateProxy(&listener); |
+ RunProxy(); |
+ |
+ // Use an interface requested via another interface. On the remote end both |
+ // interfaces are bound on the proxy thread. This ensures that the Ping |
+ // message we send will still be dispatched properly even though the remote |
+ // endpoint may not have been bound yet by the time the message is initially |
+ // processed on the IO thread. |
+ IPC::mojom::IndirectTestDriverAssociatedPtr driver; |
+ IPC::mojom::PingReceiverAssociatedPtr ping_receiver; |
+ proxy()->GetRemoteAssociatedInterface(&driver); |
+ driver->GetPingReceiver( |
+ mojo::GetProxy(&ping_receiver, driver.associated_group())); |
+ |
+ base::RunLoop loop; |
+ ping_receiver->Ping(loop.QuitClosure()); |
+ loop.Run(); |
+ |
+ DestroyProxy(); |
+} |
+ |
class ListenerWithSyncAssociatedInterface |
: public IPC::Listener, |
public IPC::mojom::SimpleTestDriver { |
@@ -1159,6 +1252,87 @@ DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT(SyncAssociatedInterface, |
DestroyProxy(); |
} |
+TEST_F(IPCChannelProxyMojoTest, CreatePaused) { |
+ // Ensures that creating a paused channel elicits the expected behavior when |
+ // sending messages, unpausing, sending more messages, and then manually |
+ // flushing. Specifically a sequence like: |
+ // |
+ // Connect() |
+ // Send(A) |
+ // Send(B) |
+ // Unpause(false) |
+ // Send(C) |
+ // Flush() |
+ // |
+ // must result in the other end receiving messages C, A, and then B, in that |
+ // order. |
+ // |
+ // This behavior is required by some consumers of IPC::Channel, and it is not |
+ // sufficient to leave this up to the consumer to implement since associated |
+ // interface requests and messages also need to be queued according to the |
+ // same policy. |
+ InitWithMojo("CreatePausedClient"); |
+ |
+ DummyListener listener; |
+ CreateProxy(&listener); |
+ RunProxy(true /* create_paused */); |
+ |
+ // These messages must be queued internally since the channel is paused. |
+ SendValue(proxy(), 1); |
+ SendValue(proxy(), 2); |
+ |
+ proxy()->Unpause(false /* flush */); |
+ |
+ // These messages must be sent immediately since the channel is unpaused. |
+ SendValue(proxy(), 3); |
+ SendValue(proxy(), 4); |
+ |
+ // Now we flush the previously queued messages. |
+ proxy()->Flush(); |
+ |
+ EXPECT_TRUE(WaitForClientShutdown()); |
+ DestroyProxy(); |
+} |
+ |
+class ExpectValueSequenceListener : public IPC::Listener { |
+ public: |
+ explicit ExpectValueSequenceListener(std::queue<int32_t>* expected_values) |
+ : expected_values_(expected_values) {} |
+ ~ExpectValueSequenceListener() override {} |
+ |
+ // IPC::Listener: |
+ bool OnMessageReceived(const IPC::Message& message) override { |
+ DCHECK(!expected_values_->empty()); |
+ base::PickleIterator iter(message); |
+ int32_t should_be_expected; |
+ EXPECT_TRUE(iter.ReadInt(&should_be_expected)); |
+ EXPECT_EQ(expected_values_->front(), should_be_expected); |
+ expected_values_->pop(); |
+ if (expected_values_->empty()) |
+ base::MessageLoop::current()->QuitWhenIdle(); |
+ return true; |
+ } |
+ |
+ private: |
+ std::queue<int32_t>* expected_values_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(ExpectValueSequenceListener); |
+}; |
+ |
+DEFINE_IPC_CHANNEL_MOJO_TEST_CLIENT(CreatePausedClient, ChannelProxyClient) { |
+ std::queue<int32_t> expected_values; |
+ ExpectValueSequenceListener listener(&expected_values); |
+ CreateProxy(&listener); |
+ expected_values.push(3); |
+ expected_values.push(4); |
+ expected_values.push(1); |
+ expected_values.push(2); |
+ RunProxy(); |
+ base::RunLoop().Run(); |
+ EXPECT_TRUE(expected_values.empty()); |
+ DestroyProxy(); |
+} |
+ |
#if defined(OS_POSIX) |
class ListenerThatExpectsFile : public IPC::Listener { |