Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(116)

Side by Side Diff: util/mach/mach_message_server_test.cc

Issue 544393002: Add MachMessageServer and its test (Closed) Base URL: https://chromium.googlesource.com/crashpad/crashpad@master
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "util/mach/mach_message_server.h"
16
17 #include <mach/mach.h>
18 #include <string.h>
19
20 #include "base/basictypes.h"
21 #include "gtest/gtest.h"
22 #include "util/file/fd_io.h"
23 #include "util/test/errors.h"
24 #include "util/test/mac/mach_errors.h"
25 #include "util/test/mac/mach_multiprocess.h"
26
27 namespace {
28
29 using namespace crashpad;
30 using namespace crashpad::test;
31
32 class TestMachMessageServer : public MachMessageServerInterface,
33 public MachMultiprocess {
34 public:
35 // Variations on the MachMessageServer test.
36 enum TestType {
Robert Sesek 2014/09/08 16:28:47 No tests for the error reply scenario? What about
37 // The client sends one message to the server, which will wait indefinitely
38 // in blocking mode for it.
39 kBasic = 0,
40
41 // The server waits in nonblocking mode and the client sends nothing, so
42 // the server should return immediately without processing any message.
43 kNonblockingNoMessage,
44
45 // The server waits in blocking mode for one message, but with a timeout.
46 // The client sends no message, so the server returns after the timeout.
47 kTimeoutNoMessage,
48
49 // The client sends one message to the server and then signals the server
50 // that it’s safe to start waiting for it in nonblocking mode. The message
51 // is in the server’s queue, so it’s able to receive it when it begins
52 // listening in nonblocking mode.
53 kNonblocking,
54
55 // The client sends one message to the server, which will wait in blocking
56 // mode for it up to a specific timeout.
57 kTimeout,
58
59 // The server waits for as many messages as it can receive in blocking mode
60 // with a timeout. The client sends several messages, and the server
61 // processes them all.
62 kPersistentTenMessages,
63
64 // The client sends several messages to the server and then signals the
65 // server that it’s safe to start waiting for them in nonblocking mode. The
66 // server then listens for them in nonblocking persistent mode, and receives
67 // all of them because they’ve been queued up. The client doesn’t wait for
68 // the replies until after it’s put all of its requests into the server’s
69 // queue.
70 //
71 // This test is sensitive to the length of the IPC queue limit. Mach ports
72 // normally have a queue length limit of MACH_PORT_QLIMIT_DEFAULT (which
73 // is MACH_PORT_QLIMIT_BASIC, or 5). The number of messages sent for this
74 // test must be below this, because the server does not begin dequeueing
75 // request messages until the client has finished sending them.
76 kPersistentNonblockingFourMessages,
77 };
78
79 explicit TestMachMessageServer(TestType test_type)
80 : MachMessageServerInterface(),
81 MachMultiprocess(),
82 test_type_(test_type) {
83 }
84
85 // Runs the test, verifying that the number or requests and replies begins
86 // and remains equal. Returns the number of round-trip messages processed.
87 uint32_t Test() {
88 EXPECT_EQ(requests_, replies_);
89 uint32_t start = requests_;
90
91 Run();
92
93 EXPECT_EQ(requests_, replies_);
94 return requests_ - start;
95 }
96
97 // MachMessageServerInterface:
98
99 virtual bool MachMessageServerFunction(
100 mach_msg_header_t* in,
101 mach_msg_header_t* out,
102 bool* destroy_complex_request) override {
103 *destroy_complex_request = true;
104
105 switch (test_type_) {
106 case kNonblockingNoMessage:
107 case kTimeoutNoMessage:
108 // These test types should not result in any messages being processed.
109 ADD_FAILURE();
110 return false;
111
112 default:
113 break;
114 }
115
116 const ReceiveRequestMessage* request =
117 reinterpret_cast<ReceiveRequestMessage*>(in);
118 EXPECT_EQ(RemotePort(), request->header.msgh_remote_port);
119 EXPECT_EQ(sizeof(SendRequestMessage), request->header.msgh_size);
120 EXPECT_EQ(requests_, request->number);
121 ++requests_;
122
123 SendReplyMessage* reply =
124 reinterpret_cast<SendReplyMessage*>(out);
125 reply->header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
126 reply->header.msgh_remote_port = request->header.msgh_remote_port;
127 reply->header.msgh_size = sizeof(SendReplyMessage);
128 reply->header.msgh_local_port = MACH_PORT_NULL;
129 reply->number = replies_++;
130
131 return true;
132 }
133
134 virtual mach_msg_size_t MachMessageServerRequestSize() override {
135 return sizeof(SendRequestMessage);
136 }
137
138 virtual mach_msg_size_t MachMessageServerReplySize() override {
139 return sizeof(SendReplyMessage);
140 }
141
142 private:
143 struct SendRequestMessage {
144 mach_msg_header_t header;
145 uint32_t number;
146 };
147
148 struct ReceiveRequestMessage : public SendRequestMessage {
149 mach_msg_trailer_t trailer;
150 };
151
152 struct SendReplyMessage {
153 mach_msg_header_t header;
154 uint32_t number;
155 };
156
157 struct ReceiveReplyMessage : public SendReplyMessage {
158 mach_msg_trailer_t trailer;
159 };
160
161 // MachMultiprocess:
162
163 virtual void MachMultiprocessParent() override {
164 switch (test_type_) {
165 case kNonblocking:
166 case kPersistentNonblockingFourMessages: {
167 // Wait until the child is done sending what it’s going to send.
168 char c;
169 ssize_t rv = ReadFD(ReadPipeFD(), &c, 1);
170 EXPECT_EQ(1, rv) << ErrnoMessage("read");
171 EXPECT_EQ('\0', c);
172 break;
173 }
174
175 default:
176 break;
177 }
178
179 bool persistent = false;
180 bool nonblocking = false;
181 mach_msg_timeout_t timeout = MACH_MSG_TIMEOUT_NONE;
182 kern_return_t expect = KERN_SUCCESS;
183
184 switch (test_type_) {
185 case kNonblockingNoMessage:
186 nonblocking = true;
187 expect = MACH_RCV_TIMED_OUT;
188 break;
189 case kTimeoutNoMessage:
190 timeout = 10; // milliseconds
191 expect = MACH_RCV_TIMED_OUT;
192 break;
193 case kNonblocking:
194 nonblocking = true;
195 break;
196 case kTimeout:
197 timeout = 10; // milliseconds
198 break;
199 case kPersistentTenMessages:
200 persistent = true;
201 timeout = 10; // milliseconds
202 expect = MACH_RCV_TIMED_OUT;
203 break;
204 case kPersistentNonblockingFourMessages:
205 persistent = true;
206 nonblocking = true;
207 expect = MACH_RCV_TIMED_OUT;
208 break;
209 default:
210 break;
211 }
212
213 kern_return_t kr;
214 ASSERT_EQ(expect, (kr = MachMessageServer(this,
215 LocalPort(),
216 MACH_MSG_OPTION_NONE,
217 persistent,
218 nonblocking,
219 timeout)))
220 << MachErrorMessage(kr, "MachMessageServer");
221 }
222
223 virtual void MachMultiprocessChild() override {
Robert Sesek 2014/09/08 16:28:47 The test is pretty comprehensive, which is good, b
224 size_t count = 1;
225 switch (test_type_) {
226 case kNonblockingNoMessage:
227 case kTimeoutNoMessage:
228 count = 0;
229 break;
230 case kPersistentTenMessages:
231 count = 10;
232 break;
233 case kPersistentNonblockingFourMessages:
234 count = 4;
235 break;
236 default:
237 break;
238 }
239
240 for (size_t index = 0; index < count; ++index) {
241 if (test_type_ == kPersistentNonblockingFourMessages) {
242 // For this test, all of the messages need to go into the queue before
243 // the parent is allowed to start processing them, so the replies won’t
244 // be processed until all of the requests are sent.
245 ChildSendRequest();
246 } else {
247 ChildSendRequestAndWaitForReply();
248 }
249 if (testing::Test::HasFatalFailure()) {
250 return;
251 }
252 }
253
254 if (test_type_ == kPersistentNonblockingFourMessages) {
255 // Now that all of the requests have been sent, let the parent know that
256 // it’s safe to begin processing them, and then wait for the replies.
257 ChildNotifyParentViaPipe();
258 if (testing::Test::HasFatalFailure()) {
259 return;
260 }
261
262 for (size_t index = 0; index < count; ++index) {
263 ChildWaitForReply();
264 if (testing::Test::HasFatalFailure()) {
265 return;
266 }
267 }
268 }
269 }
270
271 // In the child process, sends a request message to the server.
272 void ChildSendRequest() {
273 SendRequestMessage request = {};
274 request.header.msgh_bits =
275 MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND);
276 request.header.msgh_size = sizeof(request);
277 request.header.msgh_remote_port = RemotePort();
278 request.header.msgh_local_port = LocalPort();
279 request.number = requests_++;
280
281 kern_return_t kr = mach_msg(&request.header,
282 MACH_SEND_MSG | MACH_SEND_TIMEOUT,
283 request.header.msgh_size,
284 0,
285 MACH_PORT_NULL,
286 MACH_MSG_TIMEOUT_NONE,
287 MACH_PORT_NULL);
288 ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg");
289 }
290
291 // In the child process, waits for a reply message from the server.
292 void ChildWaitForReply() {
293 ReceiveReplyMessage reply = {};
294 kern_return_t kr = mach_msg(&reply.header,
295 MACH_RCV_MSG,
296 0,
297 sizeof(reply),
298 LocalPort(),
299 MACH_MSG_TIMEOUT_NONE,
300 MACH_PORT_NULL);
301 ASSERT_EQ(MACH_MSG_SUCCESS, kr) << MachErrorMessage(kr, "mach_msg");
302
303 ASSERT_EQ(static_cast<mach_port_t>(MACH_PORT_NULL),
304 reply.header.msgh_remote_port);
305 ASSERT_EQ(sizeof(SendReplyMessage), reply.header.msgh_size);
306 ASSERT_EQ(replies_, reply.number);
307 ++replies_;
308 }
309
310 // For test types where the child needs to notify the server in the parent
311 // that the child is ready, this method will send a byte via the POSIX pipe.
312 // The parent will be waiting in a read() on this pipe, and will proceed to
313 // running MachMessageServer() once it’s received.
314 void ChildNotifyParentViaPipe() {
315 char c = '\0';
316 ssize_t rv = WriteFD(WritePipeFD(), &c, 1);
317 ASSERT_EQ(1, rv) << ErrnoMessage("write");
318 }
319
320 // In the child process, sends a request message to the server and then
321 // receives a reply message.
322 void ChildSendRequestAndWaitForReply() {
323 ChildSendRequest();
324 if (testing::Test::HasFatalFailure()) {
325 return;
326 }
327
328 if (test_type_ == kNonblocking) {
329 // The parent is waiting to read a byte to indicate that the message has
330 // been placed in the queue.
331 ChildNotifyParentViaPipe();
332 if (testing::Test::HasFatalFailure()) {
333 return;
334 }
335 }
336
337 ChildWaitForReply();
338 }
339
340 TestType test_type_;
341
342 static uint32_t requests_;
343 static uint32_t replies_;
344
345 DISALLOW_COPY_AND_ASSIGN(TestMachMessageServer);
346 };
347
348 uint32_t TestMachMessageServer::requests_;
349 uint32_t TestMachMessageServer::replies_;
350
351 TEST(MachMessageServer, Basic) {
352 TestMachMessageServer test_mach_message_server(TestMachMessageServer::kBasic);
353 EXPECT_EQ(1u, test_mach_message_server.Test());
354 }
355
356 TEST(MachMessageServer, NonblockingNoMessage) {
357 TestMachMessageServer test_mach_message_server(
358 TestMachMessageServer::kNonblockingNoMessage);
359 EXPECT_EQ(0u, test_mach_message_server.Test());
360 }
361
362 TEST(MachMessageServer, TimeoutNoMessage) {
363 TestMachMessageServer test_mach_message_server(
364 TestMachMessageServer::kTimeoutNoMessage);
365 EXPECT_EQ(0u, test_mach_message_server.Test());
366 }
367
368 TEST(MachMessageServer, Nonblocking) {
369 TestMachMessageServer test_mach_message_server(
370 TestMachMessageServer::kNonblocking);
371 EXPECT_EQ(1u, test_mach_message_server.Test());
372 }
373
374 TEST(MachMessageServer, PersistentTenMessages) {
375 TestMachMessageServer test_mach_message_server(
376 TestMachMessageServer::kPersistentTenMessages);
377 EXPECT_EQ(10u, test_mach_message_server.Test());
378 }
379
380 TEST(MachMessageServer, PersistentNonblockingFourMessages) {
381 TestMachMessageServer test_mach_message_server(
382 TestMachMessageServer::kPersistentNonblockingFourMessages);
383 EXPECT_EQ(4u, test_mach_message_server.Test());
384 }
385
386 } // namespace
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698