1 // Copyright 2014 The Chromium Authors
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/run_loop.h"
12 #include "base/task/single_thread_task_runner.h"
13 #include "base/test/task_environment.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_receiver.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 mojo::PendingAssociatedRemote<IPC::mojom::Channel> sender;
30 bootstrap_->Connect(&sender, &receiver_);
31 sender_.Bind(std::move(sender));
32 sender_->SetPeerPid(sender_id);
33
34 // It's OK to start receiving right away even though `receiver_` isn't
35 // bound, because all of these tests are single-threaded and it will be
36 // bound before any incoming messages can be scheduled for processing.
37 bootstrap_->StartReceiving();
38 }
39
TakeReceiver(mojo::PendingAssociatedReceiver<IPC::mojom::Channel> * receiver)40 void TakeReceiver(
41 mojo::PendingAssociatedReceiver<IPC::mojom::Channel>* receiver) {
42 *receiver = std::move(receiver_);
43 }
44
GetSender()45 mojo::AssociatedRemote<IPC::mojom::Channel>& GetSender() { return sender_; }
46
47 private:
48 mojo::AssociatedRemote<IPC::mojom::Channel> sender_;
49 mojo::PendingAssociatedReceiver<IPC::mojom::Channel> receiver_;
50 std::unique_ptr<IPC::MojoBootstrap> bootstrap_;
51 };
52
53 class PeerPidReceiver : public IPC::mojom::Channel {
54 public:
55 enum class MessageExpectation {
56 kNotExpected,
57 kExpectedValid,
58 kExpectedInvalid
59 };
60
PeerPidReceiver(mojo::PendingAssociatedReceiver<IPC::mojom::Channel> receiver,base::OnceClosure on_peer_pid_set,MessageExpectation message_expectation=MessageExpectation::kNotExpected)61 PeerPidReceiver(
62 mojo::PendingAssociatedReceiver<IPC::mojom::Channel> receiver,
63 base::OnceClosure on_peer_pid_set,
64 MessageExpectation message_expectation = MessageExpectation::kNotExpected)
65 : receiver_(this, std::move(receiver)),
66 on_peer_pid_set_(std::move(on_peer_pid_set)),
67 message_expectation_(message_expectation) {
68 receiver_.set_disconnect_handler(disconnect_run_loop_.QuitClosure());
69 }
70
71 PeerPidReceiver(const PeerPidReceiver&) = delete;
72 PeerPidReceiver& operator=(const PeerPidReceiver&) = delete;
73
~PeerPidReceiver()74 ~PeerPidReceiver() override {
75 bool expected_message =
76 message_expectation_ != MessageExpectation::kNotExpected;
77 EXPECT_EQ(expected_message, received_message_);
78 }
79
80 // mojom::Channel:
SetPeerPid(int32_t pid)81 void SetPeerPid(int32_t pid) override {
82 peer_pid_ = pid;
83 std::move(on_peer_pid_set_).Run();
84 }
85
Receive(IPC::MessageView message_view)86 void Receive(IPC::MessageView message_view) override {
87 ASSERT_NE(MessageExpectation::kNotExpected, message_expectation_);
88 received_message_ = true;
89
90 IPC::Message message(
91 reinterpret_cast<const char*>(message_view.bytes().data()),
92 message_view.bytes().size());
93 bool expected_valid =
94 message_expectation_ == MessageExpectation::kExpectedValid;
95 EXPECT_EQ(expected_valid, message.IsValid());
96 }
97
GetAssociatedInterface(mojo::GenericPendingAssociatedReceiver receiver)98 void GetAssociatedInterface(
99 mojo::GenericPendingAssociatedReceiver receiver) override {}
100
peer_pid() const101 int32_t peer_pid() const { return peer_pid_; }
102
RunUntilDisconnect()103 void RunUntilDisconnect() { disconnect_run_loop_.Run(); }
104
105 private:
106 mojo::AssociatedReceiver<IPC::mojom::Channel> receiver_;
107 base::OnceClosure on_peer_pid_set_;
108 MessageExpectation message_expectation_;
109 int32_t peer_pid_ = -1;
110 bool received_message_ = false;
111 base::RunLoop disconnect_run_loop_;
112 };
113
114 class IPCMojoBootstrapTest : public testing::Test {
115 protected:
116 mojo::core::test::MultiprocessTestHelper helper_;
117 };
118
TEST_F(IPCMojoBootstrapTest,Connect)119 TEST_F(IPCMojoBootstrapTest, Connect) {
120 base::test::SingleThreadTaskEnvironment task_environment;
121 Connection connection(IPC::MojoBootstrap::Create(
122 helper_.StartChild("IPCMojoBootstrapTestClient"),
123 IPC::Channel::MODE_SERVER,
124 base::SingleThreadTaskRunner::GetCurrentDefault(),
125 base::SingleThreadTaskRunner::GetCurrentDefault()),
126 kTestServerPid);
127
128 mojo::PendingAssociatedReceiver<IPC::mojom::Channel> receiver;
129 connection.TakeReceiver(&receiver);
130
131 base::RunLoop run_loop;
132 PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure());
133 run_loop.Run();
134
135 EXPECT_EQ(kTestClientPid, impl.peer_pid());
136
137 impl.RunUntilDisconnect();
138 EXPECT_TRUE(helper_.WaitForChildTestShutdown());
139 }
140
141 // A long running process that connects to us.
MULTIPROCESS_TEST_MAIN_WITH_SETUP(IPCMojoBootstrapTestClientTestChildMain,::mojo::core::test::MultiprocessTestHelper::ChildSetup)142 MULTIPROCESS_TEST_MAIN_WITH_SETUP(
143 IPCMojoBootstrapTestClientTestChildMain,
144 ::mojo::core::test::MultiprocessTestHelper::ChildSetup) {
145 base::test::SingleThreadTaskEnvironment task_environment;
146 Connection connection(
147 IPC::MojoBootstrap::Create(
148 std::move(mojo::core::test::MultiprocessTestHelper::primordial_pipe),
149 IPC::Channel::MODE_CLIENT,
150 base::SingleThreadTaskRunner::GetCurrentDefault(),
151 base::SingleThreadTaskRunner::GetCurrentDefault()),
152 kTestClientPid);
153
154 mojo::PendingAssociatedReceiver<IPC::mojom::Channel> receiver;
155 connection.TakeReceiver(&receiver);
156
157 base::RunLoop run_loop;
158 PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure());
159 run_loop.Run();
160
161 EXPECT_EQ(kTestServerPid, impl.peer_pid());
162
163 return 0;
164 }
165
TEST_F(IPCMojoBootstrapTest,ReceiveEmptyMessage)166 TEST_F(IPCMojoBootstrapTest, ReceiveEmptyMessage) {
167 base::test::SingleThreadTaskEnvironment task_environment;
168 Connection connection(
169 IPC::MojoBootstrap::Create(
170 helper_.StartChild("IPCMojoBootstrapTestEmptyMessage"),
171 IPC::Channel::MODE_SERVER,
172 base::SingleThreadTaskRunner::GetCurrentDefault(),
173 base::SingleThreadTaskRunner::GetCurrentDefault()),
174 kTestServerPid);
175
176 mojo::PendingAssociatedReceiver<IPC::mojom::Channel> receiver;
177 connection.TakeReceiver(&receiver);
178
179 base::RunLoop run_loop;
180 PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure(),
181 PeerPidReceiver::MessageExpectation::kExpectedInvalid);
182 run_loop.Run();
183
184 // Wait for the Channel to be disconnected so we can reasonably assert that
185 // the child's empty message must have been received before we pass the test.
186 impl.RunUntilDisconnect();
187
188 EXPECT_TRUE(helper_.WaitForChildTestShutdown());
189 }
190
191 // A long running process that connects to us.
MULTIPROCESS_TEST_MAIN_WITH_SETUP(IPCMojoBootstrapTestEmptyMessageTestChildMain,::mojo::core::test::MultiprocessTestHelper::ChildSetup)192 MULTIPROCESS_TEST_MAIN_WITH_SETUP(
193 IPCMojoBootstrapTestEmptyMessageTestChildMain,
194 ::mojo::core::test::MultiprocessTestHelper::ChildSetup) {
195 base::test::SingleThreadTaskEnvironment task_environment;
196 Connection connection(
197 IPC::MojoBootstrap::Create(
198 std::move(mojo::core::test::MultiprocessTestHelper::primordial_pipe),
199 IPC::Channel::MODE_CLIENT,
200 base::SingleThreadTaskRunner::GetCurrentDefault(),
201 base::SingleThreadTaskRunner::GetCurrentDefault()),
202 kTestClientPid);
203
204 mojo::PendingAssociatedReceiver<IPC::mojom::Channel> receiver;
205 connection.TakeReceiver(&receiver);
206 auto& sender = connection.GetSender();
207
208 uint8_t data = 0;
209 sender->Receive(
210 IPC::MessageView(base::make_span(&data, 0u), std::nullopt /* handles */));
211
212 base::RunLoop run_loop;
213 PeerPidReceiver impl(std::move(receiver), run_loop.QuitClosure());
214 run_loop.Run();
215
216 return 0;
217 }
218
219 } // namespace
220