| // Copyright 2016 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 <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/pickle.h" |
| #include "base/run_loop.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/task/post_task.h" |
| #include "base/threading/thread.h" |
| #include "content/public/browser/browser_associated_interface.h" |
| #include "content/public/browser/browser_message_filter.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/test/test_browser_thread_bundle.h" |
| #include "content/test/test_browser_associated_interfaces.mojom.h" |
| #include "ipc/ipc_channel_factory.h" |
| #include "ipc/ipc_channel_mojo.h" |
| #include "ipc/ipc_channel_proxy.h" |
| #include "ipc/ipc_listener.h" |
| #include "ipc/ipc_message.h" |
| #include "mojo/public/cpp/system/message_pipe.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace content { |
| |
| const int kNumTestMessages = 100; |
| |
| class BrowserAssociatedInterfaceTest : public testing::Test { |
| public: |
| static void AddFilterToChannel(BrowserMessageFilter* filter, |
| IPC::ChannelProxy* channel) { |
| filter->RegisterAssociatedInterfaces(channel); |
| channel->AddFilter(filter->GetFilter()); |
| } |
| }; |
| |
| class ProxyRunner : public IPC::Listener { |
| public: |
| ProxyRunner(mojo::ScopedMessagePipeHandle pipe, |
| bool for_server, |
| scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner) { |
| std::unique_ptr<IPC::ChannelFactory> factory; |
| if (for_server) { |
| factory = IPC::ChannelMojo::CreateServerFactory( |
| std::move(pipe), ipc_task_runner, |
| base::ThreadTaskRunnerHandle::Get()); |
| } else { |
| factory = IPC::ChannelMojo::CreateClientFactory( |
| std::move(pipe), ipc_task_runner, |
| base::ThreadTaskRunnerHandle::Get()); |
| } |
| channel_ = |
| IPC::ChannelProxy::Create(std::move(factory), this, ipc_task_runner, |
| base::ThreadTaskRunnerHandle::Get()); |
| } |
| |
| void ShutDown() { channel_.reset(); } |
| |
| IPC::ChannelProxy* channel() { return channel_.get(); } |
| |
| private: |
| // IPC::Listener: |
| bool OnMessageReceived(const IPC::Message& message) override { return false; } |
| |
| std::unique_ptr<IPC::ChannelProxy> channel_; |
| }; |
| |
| class TestDriverMessageFilter |
| : public BrowserMessageFilter, |
| public BrowserAssociatedInterface< |
| mojom::BrowserAssociatedInterfaceTestDriver>, |
| public mojom::BrowserAssociatedInterfaceTestDriver { |
| public: |
| TestDriverMessageFilter() |
| : BrowserMessageFilter(0), |
| BrowserAssociatedInterface<mojom::BrowserAssociatedInterfaceTestDriver>( |
| this, this) { |
| } |
| |
| private: |
| ~TestDriverMessageFilter() override {} |
| |
| // BrowserMessageFilter: |
| bool OnMessageReceived(const IPC::Message& message) override { |
| std::string actual_string; |
| base::PickleIterator iter(message); |
| EXPECT_TRUE(iter.ReadString(&actual_string)); |
| EXPECT_EQ(next_expected_string_, actual_string); |
| message_count_++; |
| return true; |
| } |
| |
| void OnFilterRemoved() override { |
| // Check that the bindings are cleared by |
| // BrowserAssociatedInterface::ClearBindings() callbacks. |
| EXPECT_FALSE(internal_state_->bindings_.has_value()); |
| } |
| |
| // mojom::BrowserAssociatedInterfaceTestDriver: |
| void ExpectString(const std::string& expected) override { |
| next_expected_string_ = expected; |
| } |
| |
| void RequestQuit(RequestQuitCallback callback) override { |
| EXPECT_EQ(kNumTestMessages, message_count_); |
| std::move(callback).Run(); |
| base::RunLoop::QuitCurrentWhenIdleDeprecated(); |
| } |
| |
| std::string next_expected_string_; |
| int message_count_ = 0; |
| }; |
| |
| class TestClientRunner { |
| public: |
| explicit TestClientRunner(mojo::ScopedMessagePipeHandle pipe) |
| : client_thread_("Test client") { |
| client_thread_.Start(); |
| client_thread_.task_runner()->PostTask( |
| FROM_HERE, base::BindOnce(&RunTestClient, std::move(pipe))); |
| } |
| |
| ~TestClientRunner() { |
| client_thread_.Stop(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| private: |
| static void RunTestClient(mojo::ScopedMessagePipeHandle pipe) { |
| base::Thread io_thread("Client IO thread"); |
| io_thread.StartWithOptions( |
| base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); |
| ProxyRunner proxy(std::move(pipe), false, io_thread.task_runner()); |
| |
| mojom::BrowserAssociatedInterfaceTestDriverAssociatedPtr driver; |
| proxy.channel()->GetRemoteAssociatedInterface(&driver); |
| |
| for (int i = 0; i < kNumTestMessages; ++i) { |
| std::string next_message = base::StringPrintf("test %d", i); |
| driver->ExpectString(next_message); |
| |
| std::unique_ptr<IPC::Message> message(new IPC::Message); |
| message->WriteString(next_message); |
| proxy.channel()->Send(message.release()); |
| } |
| |
| driver->RequestQuit(base::RunLoop::QuitCurrentWhenIdleClosureDeprecated()); |
| |
| base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).Run(); |
| |
| proxy.ShutDown(); |
| io_thread.Stop(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| base::Thread client_thread_; |
| }; |
| |
| TEST_F(BrowserAssociatedInterfaceTest, Basic) { |
| TestBrowserThreadBundle browser_threads_; |
| mojo::MessagePipe pipe; |
| ProxyRunner proxy( |
| std::move(pipe.handle0), true, |
| base::CreateSingleThreadTaskRunnerWithTraits({BrowserThread::IO})); |
| AddFilterToChannel(new TestDriverMessageFilter, proxy.channel()); |
| |
| TestClientRunner client(std::move(pipe.handle1)); |
| base::RunLoop().Run(); |
| |
| proxy.ShutDown(); |
| base::RunLoop().RunUntilIdle(); |
| } |
| |
| } // namespace content |