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