1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <memory>
6 #include <string>
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/message_loop/message_pump_type.h"
12 #include "base/pickle.h"
13 #include "base/run_loop.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/threading/thread.h"
17 #include "content/public/browser/browser_associated_interface.h"
18 #include "content/public/browser/browser_message_filter.h"
19 #include "content/public/browser/browser_task_traits.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/test/browser_task_environment.h"
22 #include "content/test/test_browser_associated_interfaces.mojom.h"
23 #include "ipc/ipc_channel_factory.h"
24 #include "ipc/ipc_channel_mojo.h"
25 #include "ipc/ipc_channel_proxy.h"
26 #include "ipc/ipc_listener.h"
27 #include "ipc/ipc_message.h"
28 #include "mojo/public/cpp/bindings/associated_remote.h"
29 #include "mojo/public/cpp/system/message_pipe.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31
32 namespace content {
33
34 const int kNumTestMessages = 100;
35
36 class BrowserAssociatedInterfaceTest : public testing::Test {
37 public:
AddFilterToChannel(BrowserMessageFilter * filter,IPC::ChannelProxy * channel)38 static void AddFilterToChannel(BrowserMessageFilter* filter,
39 IPC::ChannelProxy* channel) {
40 filter->RegisterAssociatedInterfaces(channel);
41 channel->AddFilter(filter->GetFilter());
42 }
43 };
44
45 class ProxyRunner : public IPC::Listener {
46 public:
ProxyRunner(mojo::ScopedMessagePipeHandle pipe,bool for_server,scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner)47 ProxyRunner(mojo::ScopedMessagePipeHandle pipe,
48 bool for_server,
49 scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner) {
50 std::unique_ptr<IPC::ChannelFactory> factory;
51 if (for_server) {
52 factory = IPC::ChannelMojo::CreateServerFactory(
53 std::move(pipe), ipc_task_runner,
54 base::ThreadTaskRunnerHandle::Get());
55 } else {
56 factory = IPC::ChannelMojo::CreateClientFactory(
57 std::move(pipe), ipc_task_runner,
58 base::ThreadTaskRunnerHandle::Get());
59 }
60 channel_ =
61 IPC::ChannelProxy::Create(std::move(factory), this, ipc_task_runner,
62 base::ThreadTaskRunnerHandle::Get());
63 }
64
ShutDown()65 void ShutDown() { channel_.reset(); }
66
channel()67 IPC::ChannelProxy* channel() { return channel_.get(); }
68
69 private:
70 // IPC::Listener:
OnMessageReceived(const IPC::Message & message)71 bool OnMessageReceived(const IPC::Message& message) override { return false; }
72
73 std::unique_ptr<IPC::ChannelProxy> channel_;
74 };
75
76 class TestDriverMessageFilter
77 : public BrowserMessageFilter,
78 public BrowserAssociatedInterface<
79 mojom::BrowserAssociatedInterfaceTestDriver>,
80 public mojom::BrowserAssociatedInterfaceTestDriver {
81 public:
TestDriverMessageFilter()82 TestDriverMessageFilter()
83 : BrowserMessageFilter(0),
84 BrowserAssociatedInterface<mojom::BrowserAssociatedInterfaceTestDriver>(
85 this, this) {
86 }
87
88 private:
~TestDriverMessageFilter()89 ~TestDriverMessageFilter() override {}
90
91 // BrowserMessageFilter:
OnMessageReceived(const IPC::Message & message)92 bool OnMessageReceived(const IPC::Message& message) override {
93 std::string actual_string;
94 base::PickleIterator iter(message);
95 EXPECT_TRUE(iter.ReadString(&actual_string));
96 EXPECT_EQ(next_expected_string_, actual_string);
97 message_count_++;
98 return true;
99 }
100
OnFilterRemoved()101 void OnFilterRemoved() override {
102 // Check that the bindings are cleared by
103 // BrowserAssociatedInterface::ClearReceivers() callbacks.
104 EXPECT_FALSE(internal_state_->receivers_.has_value());
105 }
106
107 // mojom::BrowserAssociatedInterfaceTestDriver:
ExpectString(const std::string & expected)108 void ExpectString(const std::string& expected) override {
109 next_expected_string_ = expected;
110 }
111
RequestQuit(RequestQuitCallback callback)112 void RequestQuit(RequestQuitCallback callback) override {
113 EXPECT_EQ(kNumTestMessages, message_count_);
114 std::move(callback).Run();
115 base::RunLoop::QuitCurrentWhenIdleDeprecated();
116 }
117
118 std::string next_expected_string_;
119 int message_count_ = 0;
120 };
121
122 class TestClientRunner {
123 public:
TestClientRunner(mojo::ScopedMessagePipeHandle pipe)124 explicit TestClientRunner(mojo::ScopedMessagePipeHandle pipe)
125 : client_thread_("Test client") {
126 client_thread_.Start();
127 client_thread_.task_runner()->PostTask(
128 FROM_HERE, base::BindOnce(&RunTestClient, std::move(pipe)));
129 }
130
~TestClientRunner()131 ~TestClientRunner() {
132 client_thread_.Stop();
133 base::RunLoop().RunUntilIdle();
134 }
135
136 private:
RunTestClient(mojo::ScopedMessagePipeHandle pipe)137 static void RunTestClient(mojo::ScopedMessagePipeHandle pipe) {
138 base::Thread io_thread("Client IO thread");
139 io_thread.StartWithOptions(
140 base::Thread::Options(base::MessagePumpType::IO, 0));
141 ProxyRunner proxy(std::move(pipe), false, io_thread.task_runner());
142
143 mojo::AssociatedRemote<mojom::BrowserAssociatedInterfaceTestDriver> driver;
144 proxy.channel()->GetRemoteAssociatedInterface(&driver);
145
146 for (int i = 0; i < kNumTestMessages; ++i) {
147 std::string next_message = base::StringPrintf("test %d", i);
148 driver->ExpectString(next_message);
149
150 std::unique_ptr<IPC::Message> message(new IPC::Message);
151 message->WriteString(next_message);
152 proxy.channel()->Send(message.release());
153 }
154
155 driver->RequestQuit(base::RunLoop::QuitCurrentWhenIdleClosureDeprecated());
156
157 base::RunLoop(base::RunLoop::Type::kNestableTasksAllowed).Run();
158
159 proxy.ShutDown();
160 io_thread.Stop();
161 base::RunLoop().RunUntilIdle();
162 }
163
164 base::Thread client_thread_;
165 };
166
TEST_F(BrowserAssociatedInterfaceTest,Basic)167 TEST_F(BrowserAssociatedInterfaceTest, Basic) {
168 BrowserTaskEnvironment task_environment_;
169 mojo::MessagePipe pipe;
170 ProxyRunner proxy(std::move(pipe.handle0), true, GetIOThreadTaskRunner({}));
171 AddFilterToChannel(new TestDriverMessageFilter, proxy.channel());
172
173 TestClientRunner client(std::move(pipe.handle1));
174 base::RunLoop().Run();
175
176 proxy.ShutDown();
177 base::RunLoop().RunUntilIdle();
178 }
179
180 } // namespace content
181