1 // Copyright 2014 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 "ipc/ipc_mojo_bootstrap.h"
6
7 #include <cstdint>
8 #include <memory>
9 #include <utility>
10
11 #include "base/message_loop/message_loop.h"
12 #include "base/run_loop.h"
13 #include "base/threading/thread_task_runner_handle.h"
14 #include "ipc/ipc.mojom.h"
15 #include "ipc/ipc_test_base.h"
16 #include "mojo/core/test/multiprocess_test_helper.h"
17 #include "mojo/public/cpp/bindings/associated_binding.h"
18
19 namespace {
20
21 constexpr int32_t kTestServerPid = 42;
22 constexpr int32_t kTestClientPid = 4242;
23
24 class Connection {
25 public:
Connection(std::unique_ptr<IPC::MojoBootstrap> bootstrap,int32_t sender_id)26 explicit Connection(std::unique_ptr<IPC::MojoBootstrap> bootstrap,
27 int32_t sender_id)
28 : bootstrap_(std::move(bootstrap)) {
29 bootstrap_->Connect(&sender_, &receiver_);
30 sender_->SetPeerPid(sender_id);
31 }
32
TakeReceiver(IPC::mojom::ChannelAssociatedRequest * receiver)33 void TakeReceiver(IPC::mojom::ChannelAssociatedRequest* receiver) {
34 *receiver = std::move(receiver_);
35 }
36
GetSender()37 IPC::mojom::ChannelAssociatedPtr& GetSender() { return sender_; }
38
39 private:
40 IPC::mojom::ChannelAssociatedPtr sender_;
41 IPC::mojom::ChannelAssociatedRequest receiver_;
42 std::unique_ptr<IPC::MojoBootstrap> bootstrap_;
43 };
44
45 class PeerPidReceiver : public IPC::mojom::Channel {
46 public:
47 enum class MessageExpectation {
48 kNotExpected,
49 kExpectedValid,
50 kExpectedInvalid
51 };
52
PeerPidReceiver(IPC::mojom::ChannelAssociatedRequest request,const base::Closure & on_peer_pid_set,MessageExpectation message_expectation=MessageExpectation::kNotExpected)53 PeerPidReceiver(
54 IPC::mojom::ChannelAssociatedRequest request,
55 const base::Closure& on_peer_pid_set,
56 MessageExpectation message_expectation = MessageExpectation::kNotExpected)
57 : binding_(this, std::move(request)),
58 on_peer_pid_set_(on_peer_pid_set),
59 message_expectation_(message_expectation) {
60 binding_.set_connection_error_handler(disconnect_run_loop_.QuitClosure());
61 }
62
~PeerPidReceiver()63 ~PeerPidReceiver() override {
64 bool expected_message =
65 message_expectation_ != MessageExpectation::kNotExpected;
66 EXPECT_EQ(expected_message, received_message_);
67 }
68
69 // mojom::Channel:
SetPeerPid(int32_t pid)70 void SetPeerPid(int32_t pid) override {
71 peer_pid_ = pid;
72 on_peer_pid_set_.Run();
73 }
74
Receive(IPC::MessageView message_view)75 void Receive(IPC::MessageView message_view) override {
76 ASSERT_NE(MessageExpectation::kNotExpected, message_expectation_);
77 received_message_ = true;
78
79 IPC::Message message(message_view.data(), message_view.size());
80 bool expected_valid =
81 message_expectation_ == MessageExpectation::kExpectedValid;
82 EXPECT_EQ(expected_valid, message.IsValid());
83 }
84
GetAssociatedInterface(const std::string & name,IPC::mojom::GenericInterfaceAssociatedRequest request)85 void GetAssociatedInterface(
86 const std::string& name,
87 IPC::mojom::GenericInterfaceAssociatedRequest request) override {}
88
peer_pid() const89 int32_t peer_pid() const { return peer_pid_; }
90
RunUntilDisconnect()91 void RunUntilDisconnect() { disconnect_run_loop_.Run(); }
92
93 private:
94 mojo::AssociatedBinding<IPC::mojom::Channel> binding_;
95 const base::Closure on_peer_pid_set_;
96 MessageExpectation message_expectation_;
97 int32_t peer_pid_ = -1;
98 bool received_message_ = false;
99 base::RunLoop disconnect_run_loop_;
100
101 DISALLOW_COPY_AND_ASSIGN(PeerPidReceiver);
102 };
103
104 class IPCMojoBootstrapTest : public testing::Test {
105 protected:
106 mojo::core::test::MultiprocessTestHelper helper_;
107 };
108
TEST_F(IPCMojoBootstrapTest,Connect)109 TEST_F(IPCMojoBootstrapTest, Connect) {
110 base::MessageLoop message_loop;
111 Connection connection(
112 IPC::MojoBootstrap::Create(
113 helper_.StartChild("IPCMojoBootstrapTestClient"),
114 IPC::Channel::MODE_SERVER, base::ThreadTaskRunnerHandle::Get(),
115 base::ThreadTaskRunnerHandle::Get()),
116 kTestServerPid);
117
118 IPC::mojom::ChannelAssociatedRequest receiver;
119 connection.TakeReceiver(&receiver);
120
121 base::RunLoop run_loop;
122 PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure());
123 run_loop.Run();
124
125 EXPECT_EQ(kTestClientPid, impl.peer_pid());
126
127 impl.RunUntilDisconnect();
128 EXPECT_TRUE(helper_.WaitForChildTestShutdown());
129 }
130
131 // A long running process that connects to us.
MULTIPROCESS_TEST_MAIN_WITH_SETUP(IPCMojoBootstrapTestClientTestChildMain,::mojo::core::test::MultiprocessTestHelper::ChildSetup)132 MULTIPROCESS_TEST_MAIN_WITH_SETUP(
133 IPCMojoBootstrapTestClientTestChildMain,
134 ::mojo::core::test::MultiprocessTestHelper::ChildSetup) {
135 base::MessageLoop message_loop;
136 Connection connection(
137 IPC::MojoBootstrap::Create(
138 std::move(mojo::core::test::MultiprocessTestHelper::primordial_pipe),
139 IPC::Channel::MODE_CLIENT, base::ThreadTaskRunnerHandle::Get(),
140 base::ThreadTaskRunnerHandle::Get()),
141 kTestClientPid);
142
143 IPC::mojom::ChannelAssociatedRequest receiver;
144 connection.TakeReceiver(&receiver);
145
146 base::RunLoop run_loop;
147 PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure());
148 run_loop.Run();
149
150 EXPECT_EQ(kTestServerPid, impl.peer_pid());
151
152 return 0;
153 }
154
TEST_F(IPCMojoBootstrapTest,ReceiveEmptyMessage)155 TEST_F(IPCMojoBootstrapTest, ReceiveEmptyMessage) {
156 base::MessageLoop message_loop;
157 Connection connection(
158 IPC::MojoBootstrap::Create(
159 helper_.StartChild("IPCMojoBootstrapTestEmptyMessage"),
160 IPC::Channel::MODE_SERVER, base::ThreadTaskRunnerHandle::Get(),
161 base::ThreadTaskRunnerHandle::Get()),
162 kTestServerPid);
163
164 IPC::mojom::ChannelAssociatedRequest receiver;
165 connection.TakeReceiver(&receiver);
166
167 base::RunLoop run_loop;
168 PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure(),
169 PeerPidReceiver::MessageExpectation::kExpectedInvalid);
170 run_loop.Run();
171
172 // Wait for the Channel to be disconnected so we can reasonably assert that
173 // the child's empty message must have been received before we pass the test.
174 impl.RunUntilDisconnect();
175
176 EXPECT_TRUE(helper_.WaitForChildTestShutdown());
177 }
178
179 // A long running process that connects to us.
MULTIPROCESS_TEST_MAIN_WITH_SETUP(IPCMojoBootstrapTestEmptyMessageTestChildMain,::mojo::core::test::MultiprocessTestHelper::ChildSetup)180 MULTIPROCESS_TEST_MAIN_WITH_SETUP(
181 IPCMojoBootstrapTestEmptyMessageTestChildMain,
182 ::mojo::core::test::MultiprocessTestHelper::ChildSetup) {
183 base::MessageLoop message_loop;
184 Connection connection(
185 IPC::MojoBootstrap::Create(
186 std::move(mojo::core::test::MultiprocessTestHelper::primordial_pipe),
187 IPC::Channel::MODE_CLIENT, base::ThreadTaskRunnerHandle::Get(),
188 base::ThreadTaskRunnerHandle::Get()),
189 kTestClientPid);
190
191 IPC::mojom::ChannelAssociatedRequest receiver;
192 connection.TakeReceiver(&receiver);
193 auto& sender = connection.GetSender();
194
195 uint8_t data = 0;
196 sender->Receive(
197 IPC::MessageView(mojo_base::BigBufferView(base::make_span(&data, 0)),
198 base::nullopt /* handles */));
199
200 base::RunLoop run_loop;
201 PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure());
202 run_loop.Run();
203
204 return 0;
205 }
206
207 } // namespace
208