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 "mojo/edk/embedder/embedder.h" 6 7 #include <stddef.h> 8 #include <stdint.h> 9 #include <string.h> 10 11 #include <utility> 12 13 #include "base/bind.h" 14 #include "base/command_line.h" 15 #include "base/files/file.h" 16 #include "base/logging.h" 17 #include "base/macros.h" 18 #include "base/memory/shared_memory.h" 19 #include "base/message_loop/message_loop.h" 20 #include "base/process/process_handle.h" 21 #include "base/synchronization/waitable_event.h" 22 #include "base/test/test_timeouts.h" 23 #include "mojo/edk/embedder/platform_channel_pair.h" 24 #include "mojo/edk/embedder/test_embedder.h" 25 #include "mojo/edk/system/test_utils.h" 26 #include "mojo/edk/test/mojo_test_base.h" 27 #include "mojo/public/c/system/core.h" 28 #include "mojo/public/cpp/system/handle.h" 29 #include "mojo/public/cpp/system/message_pipe.h" 30 #include "testing/gtest/include/gtest/gtest.h" 31 32 namespace mojo { 33 namespace edk { 34 namespace { 35 36 const MojoHandleSignals kSignalReadadableWritable = 37 MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE; 38 39 const MojoHandleSignals kSignalAll = MOJO_HANDLE_SIGNAL_READABLE | 40 MOJO_HANDLE_SIGNAL_WRITABLE | 41 MOJO_HANDLE_SIGNAL_PEER_CLOSED; 42 43 // The multiprocess tests that use these don't compile on iOS. 44 #if !defined(OS_IOS) 45 const char kHelloWorld[] = "hello world"; 46 const char kByeWorld[] = "bye world"; 47 #endif 48 49 using EmbedderTest = test::MojoTestBase; 50 51 TEST_F(EmbedderTest, ChannelBasic) { 52 MojoHandle server_mp, client_mp; 53 CreateMessagePipe(&server_mp, &client_mp); 54 55 const std::string kHello = "hello"; 56 57 // We can write to a message pipe handle immediately. 58 WriteMessage(server_mp, kHello); 59 EXPECT_EQ(kHello, ReadMessage(client_mp)); 60 61 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp)); 62 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp)); 63 } 64 65 // Test sending a MP which has read messages out of the OS pipe but which have 66 // not been consumed using MojoReadMessage yet. 67 TEST_F(EmbedderTest, SendReadableMessagePipe) { 68 MojoHandle server_mp, client_mp; 69 CreateMessagePipe(&server_mp, &client_mp); 70 71 MojoHandle server_mp2, client_mp2; 72 CreateMessagePipe(&server_mp2, &client_mp2); 73 74 // Write to server2 and wait for client2 to be readable before sending it. 75 // client2's MessagePipeDispatcher will have the message below in its 76 // message_queue_. For extra measures, also verify that this pending message 77 // can contain a message pipe. 78 MojoHandle server_mp3, client_mp3; 79 CreateMessagePipe(&server_mp3, &client_mp3); 80 81 const std::string kHello = "hello"; 82 WriteMessageWithHandles(server_mp2, kHello, &client_mp3, 1); 83 84 MojoHandleSignalsState state; 85 ASSERT_EQ(MOJO_RESULT_OK, MojoWait(client_mp2, MOJO_HANDLE_SIGNAL_READABLE, 86 MOJO_DEADLINE_INDEFINITE, &state)); 87 ASSERT_EQ(kSignalReadadableWritable, state.satisfied_signals); 88 ASSERT_EQ(kSignalAll, state.satisfiable_signals); 89 90 // Now send client2 91 WriteMessageWithHandles(server_mp, kHello, &client_mp2, 1); 92 93 MojoHandle port; 94 std::string message = ReadMessageWithHandles(client_mp, &port, 1); 95 EXPECT_EQ(kHello, message); 96 97 client_mp2 = port; 98 message = ReadMessageWithHandles(client_mp2, &client_mp3, 1); 99 EXPECT_EQ(kHello, message); 100 101 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp3)); 102 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp3)); 103 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp2)); 104 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp2)); 105 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp)); 106 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp)); 107 } 108 109 // Verifies that a MP with pending messages to be written can be sent and the 110 // pending messages aren't dropped. 111 TEST_F(EmbedderTest, SendMessagePipeWithWriteQueue) { 112 MojoHandle server_mp, client_mp; 113 CreateMessagePipe(&server_mp, &client_mp); 114 115 MojoHandle server_mp2, client_mp2; 116 CreateMessagePipe(&server_mp2, &client_mp2); 117 118 static const size_t kNumMessages = 1001; 119 for (size_t i = 1; i <= kNumMessages; i++) 120 WriteMessage(client_mp2, std::string(i, 'A' + (i % 26))); 121 122 // Now send client2. 123 WriteMessageWithHandles(server_mp, "hey", &client_mp2, 1); 124 client_mp2 = MOJO_HANDLE_INVALID; 125 126 // Read client2 just so we can close it later. 127 EXPECT_EQ("hey", ReadMessageWithHandles(client_mp, &client_mp2, 1)); 128 EXPECT_NE(MOJO_HANDLE_INVALID, client_mp2); 129 130 // Now verify that all the messages that were written were sent correctly. 131 for (size_t i = 1; i <= kNumMessages; i++) 132 ASSERT_EQ(std::string(i, 'A' + (i % 26)), ReadMessage(server_mp2)); 133 134 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp2)); 135 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp2)); 136 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp)); 137 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp)); 138 } 139 140 TEST_F(EmbedderTest, ChannelsHandlePassing) { 141 MojoHandle server_mp, client_mp; 142 CreateMessagePipe(&server_mp, &client_mp); 143 EXPECT_NE(server_mp, MOJO_HANDLE_INVALID); 144 EXPECT_NE(client_mp, MOJO_HANDLE_INVALID); 145 146 MojoHandle h0, h1; 147 CreateMessagePipe(&h0, &h1); 148 149 // Write a message to |h0| (attaching nothing). 150 const std::string kHello = "hello"; 151 WriteMessage(h0, kHello); 152 153 // Write one message to |server_mp|, attaching |h1|. 154 const std::string kWorld = "world!!!"; 155 WriteMessageWithHandles(server_mp, kWorld, &h1, 1); 156 h1 = MOJO_HANDLE_INVALID; 157 158 // Write another message to |h0|. 159 const std::string kFoo = "foo"; 160 WriteMessage(h0, kFoo); 161 162 // Wait for |client_mp| to become readable and read a message from it. 163 EXPECT_EQ(kWorld, ReadMessageWithHandles(client_mp, &h1, 1)); 164 EXPECT_NE(h1, MOJO_HANDLE_INVALID); 165 166 // Wait for |h1| to become readable and read a message from it. 167 EXPECT_EQ(kHello, ReadMessage(h1)); 168 169 // Wait for |h1| to become readable (again) and read its second message. 170 EXPECT_EQ(kFoo, ReadMessage(h1)); 171 172 // Write a message to |h1|. 173 const std::string kBarBaz = "barbaz"; 174 WriteMessage(h1, kBarBaz); 175 176 // Wait for |h0| to become readable and read a message from it. 177 EXPECT_EQ(kBarBaz, ReadMessage(h0)); 178 179 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(server_mp)); 180 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(client_mp)); 181 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(h0)); 182 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(h1)); 183 } 184 185 TEST_F(EmbedderTest, PipeSetup) { 186 std::string child_token = GenerateRandomToken(); 187 std::string pipe_token = GenerateRandomToken(); 188 189 ScopedMessagePipeHandle parent_mp = 190 CreateParentMessagePipe(pipe_token, child_token); 191 ScopedMessagePipeHandle child_mp = 192 CreateChildMessagePipe(pipe_token); 193 194 const std::string kHello = "hello"; 195 WriteMessage(parent_mp.get().value(), kHello); 196 197 EXPECT_EQ(kHello, ReadMessage(child_mp.get().value())); 198 } 199 200 TEST_F(EmbedderTest, PipeSetup_LaunchDeath) { 201 PlatformChannelPair pair; 202 203 std::string child_token = GenerateRandomToken(); 204 std::string pipe_token = GenerateRandomToken(); 205 206 ScopedMessagePipeHandle parent_mp = 207 CreateParentMessagePipe(pipe_token, child_token); 208 ChildProcessLaunched(base::GetCurrentProcessHandle(), pair.PassServerHandle(), 209 child_token); 210 211 // Close the remote end, simulating child death before the child connects to 212 // the reserved port. 213 ignore_result(pair.PassClientHandle()); 214 215 EXPECT_EQ(MOJO_RESULT_OK, MojoWait(parent_mp.get().value(), 216 MOJO_HANDLE_SIGNAL_PEER_CLOSED, 217 MOJO_DEADLINE_INDEFINITE, 218 nullptr)); 219 } 220 221 TEST_F(EmbedderTest, PipeSetup_LaunchFailure) { 222 PlatformChannelPair pair; 223 224 std::string child_token = GenerateRandomToken(); 225 std::string pipe_token = GenerateRandomToken(); 226 227 ScopedMessagePipeHandle parent_mp = 228 CreateParentMessagePipe(pipe_token, child_token); 229 230 ChildProcessLaunchFailed(child_token); 231 EXPECT_EQ(MOJO_RESULT_OK, MojoWait(parent_mp.get().value(), 232 MOJO_HANDLE_SIGNAL_PEER_CLOSED, 233 MOJO_DEADLINE_INDEFINITE, 234 nullptr)); 235 } 236 237 // The sequence of messages sent is: 238 // server_mp client_mp mp0 mp1 mp2 mp3 239 // 1. "hello" 240 // 2. "world!" 241 // 3. "FOO" 242 // 4. "Bar"+mp1 243 // 5. (close) 244 // 6. (close) 245 // 7. "baz" 246 // 8. (closed) 247 // 9. "quux"+mp2 248 // 10. (close) 249 // 11. (wait/cl.) 250 // 12. (wait/cl.) 251 252 #if !defined(OS_IOS) 253 254 TEST_F(EmbedderTest, MultiprocessChannels) { 255 RUN_CHILD_ON_PIPE(MultiprocessChannelsClient, server_mp) 256 // 1. Write a message to |server_mp| (attaching nothing). 257 WriteMessage(server_mp, "hello"); 258 259 // 2. Read a message from |server_mp|. 260 EXPECT_EQ("world!", ReadMessage(server_mp)); 261 262 // 3. Create a new message pipe (endpoints |mp0| and |mp1|). 263 MojoHandle mp0, mp1; 264 CreateMessagePipe(&mp0, &mp1); 265 266 // 4. Write something to |mp0|. 267 WriteMessage(mp0, "FOO"); 268 269 // 5. Write a message to |server_mp|, attaching |mp1|. 270 WriteMessageWithHandles(server_mp, "Bar", &mp1, 1); 271 mp1 = MOJO_HANDLE_INVALID; 272 273 // 6. Read a message from |mp0|, which should have |mp2| attached. 274 MojoHandle mp2 = MOJO_HANDLE_INVALID; 275 EXPECT_EQ("quux", ReadMessageWithHandles(mp0, &mp2, 1)); 276 277 // 7. Read a message from |mp2|. 278 EXPECT_EQ("baz", ReadMessage(mp2)); 279 280 // 8. Close |mp0|. 281 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(mp0)); 282 283 // 9. Tell the client to quit. 284 WriteMessage(server_mp, "quit"); 285 286 // 10. Wait on |mp2| (which should eventually fail) and then close it. 287 MojoHandleSignalsState state; 288 ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, 289 MojoWait(mp2, MOJO_HANDLE_SIGNAL_READABLE, 290 MOJO_DEADLINE_INDEFINITE, 291 &state)); 292 ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals); 293 ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals); 294 295 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(mp2)); 296 END_CHILD() 297 } 298 299 DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessChannelsClient, EmbedderTest, 300 client_mp) { 301 // 1. Read the first message from |client_mp|. 302 EXPECT_EQ("hello", ReadMessage(client_mp)); 303 304 // 2. Write a message to |client_mp| (attaching nothing). 305 WriteMessage(client_mp, "world!"); 306 307 // 4. Read a message from |client_mp|, which should have |mp1| attached. 308 MojoHandle mp1; 309 EXPECT_EQ("Bar", ReadMessageWithHandles(client_mp, &mp1, 1)); 310 311 // 5. Create a new message pipe (endpoints |mp2| and |mp3|). 312 MojoHandle mp2, mp3; 313 CreateMessagePipe(&mp2, &mp3); 314 315 // 6. Write a message to |mp3|. 316 WriteMessage(mp3, "baz"); 317 318 // 7. Close |mp3|. 319 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(mp3)); 320 321 // 8. Write a message to |mp1|, attaching |mp2|. 322 WriteMessageWithHandles(mp1, "quux", &mp2, 1); 323 mp2 = MOJO_HANDLE_INVALID; 324 325 // 9. Read a message from |mp1|. 326 EXPECT_EQ("FOO", ReadMessage(mp1)); 327 328 EXPECT_EQ("quit", ReadMessage(client_mp)); 329 330 // 10. Wait on |mp1| (which should eventually fail) and then close it. 331 MojoHandleSignalsState state; 332 ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, 333 MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE, 334 MOJO_DEADLINE_INDEFINITE, &state)); 335 ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfied_signals); 336 ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, state.satisfiable_signals); 337 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(mp1)); 338 } 339 340 TEST_F(EmbedderTest, MultiprocessBaseSharedMemory) { 341 RUN_CHILD_ON_PIPE(MultiprocessSharedMemoryClient, server_mp) 342 // 1. Create a base::SharedMemory object and create a mojo shared buffer 343 // from it. 344 base::SharedMemoryCreateOptions options; 345 options.size = 123; 346 base::SharedMemory shared_memory; 347 ASSERT_TRUE(shared_memory.Create(options)); 348 base::SharedMemoryHandle shm_handle = base::SharedMemory::DuplicateHandle( 349 shared_memory.handle()); 350 MojoHandle sb1; 351 ASSERT_EQ(MOJO_RESULT_OK, 352 CreateSharedBufferWrapper(shm_handle, 123, false, &sb1)); 353 354 // 2. Map |sb1| and write something into it. 355 char* buffer = nullptr; 356 ASSERT_EQ(MOJO_RESULT_OK, 357 MojoMapBuffer(sb1, 0, 123, reinterpret_cast<void**>(&buffer), 0)); 358 ASSERT_TRUE(buffer); 359 memcpy(buffer, kHelloWorld, sizeof(kHelloWorld)); 360 361 // 3. Duplicate |sb1| into |sb2| and pass to |server_mp|. 362 MojoHandle sb2 = MOJO_HANDLE_INVALID; 363 EXPECT_EQ(MOJO_RESULT_OK, MojoDuplicateBufferHandle(sb1, 0, &sb2)); 364 EXPECT_NE(MOJO_HANDLE_INVALID, sb2); 365 WriteMessageWithHandles(server_mp, "hello", &sb2, 1); 366 367 // 4. Read a message from |server_mp|. 368 EXPECT_EQ("bye", ReadMessage(server_mp)); 369 370 // 5. Expect that the contents of the shared buffer have changed. 371 EXPECT_EQ(kByeWorld, std::string(buffer)); 372 373 // 6. Map the original base::SharedMemory and expect it contains the 374 // expected value. 375 ASSERT_TRUE(shared_memory.Map(123)); 376 EXPECT_EQ(kByeWorld, 377 std::string(static_cast<char*>(shared_memory.memory()))); 378 379 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(sb1)); 380 END_CHILD() 381 } 382 383 DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessSharedMemoryClient, EmbedderTest, 384 client_mp) { 385 // 1. Read the first message from |client_mp|, which should have |sb1| which 386 // should be a shared buffer handle. 387 MojoHandle sb1; 388 EXPECT_EQ("hello", ReadMessageWithHandles(client_mp, &sb1, 1)); 389 390 // 2. Map |sb1|. 391 char* buffer = nullptr; 392 ASSERT_EQ(MOJO_RESULT_OK, 393 MojoMapBuffer(sb1, 0, 123, reinterpret_cast<void**>(&buffer), 0)); 394 ASSERT_TRUE(buffer); 395 396 // 3. Ensure |buffer| contains the values we expect. 397 EXPECT_EQ(kHelloWorld, std::string(buffer)); 398 399 // 4. Write into |buffer| and send a message back. 400 memcpy(buffer, kByeWorld, sizeof(kByeWorld)); 401 WriteMessage(client_mp, "bye"); 402 403 // 5. Extract the shared memory handle and ensure we can map it and read the 404 // contents. 405 base::SharedMemoryHandle shm_handle; 406 ASSERT_EQ(MOJO_RESULT_OK, 407 PassSharedMemoryHandle(sb1, &shm_handle, nullptr, nullptr)); 408 base::SharedMemory shared_memory(shm_handle, false); 409 ASSERT_TRUE(shared_memory.Map(123)); 410 EXPECT_NE(buffer, shared_memory.memory()); 411 EXPECT_EQ(kByeWorld, std::string(static_cast<char*>(shared_memory.memory()))); 412 413 // 6. Close |sb1|. Should fail because |PassSharedMemoryHandle()| should have 414 // closed the handle. 415 EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(sb1)); 416 } 417 418 #if defined(OS_MACOSX) && !defined(OS_IOS) 419 TEST_F(EmbedderTest, MultiprocessMachSharedMemory) { 420 RUN_CHILD_ON_PIPE(MultiprocessSharedMemoryClient, server_mp) 421 // 1. Create a Mach base::SharedMemory object and create a mojo shared 422 // buffer from it. 423 base::SharedMemoryCreateOptions options; 424 options.size = 123; 425 base::SharedMemory shared_memory; 426 ASSERT_TRUE(shared_memory.Create(options)); 427 base::SharedMemoryHandle shm_handle = base::SharedMemory::DuplicateHandle( 428 shared_memory.handle()); 429 MojoHandle sb1; 430 ASSERT_EQ(MOJO_RESULT_OK, 431 CreateSharedBufferWrapper(shm_handle, 123, false, &sb1)); 432 433 // 2. Map |sb1| and write something into it. 434 char* buffer = nullptr; 435 ASSERT_EQ(MOJO_RESULT_OK, 436 MojoMapBuffer(sb1, 0, 123, reinterpret_cast<void**>(&buffer), 0)); 437 ASSERT_TRUE(buffer); 438 memcpy(buffer, kHelloWorld, sizeof(kHelloWorld)); 439 440 // 3. Duplicate |sb1| into |sb2| and pass to |server_mp|. 441 MojoHandle sb2 = MOJO_HANDLE_INVALID; 442 EXPECT_EQ(MOJO_RESULT_OK, MojoDuplicateBufferHandle(sb1, 0, &sb2)); 443 EXPECT_NE(MOJO_HANDLE_INVALID, sb2); 444 WriteMessageWithHandles(server_mp, "hello", &sb2, 1); 445 446 // 4. Read a message from |server_mp|. 447 EXPECT_EQ("bye", ReadMessage(server_mp)); 448 449 // 5. Expect that the contents of the shared buffer have changed. 450 EXPECT_EQ(kByeWorld, std::string(buffer)); 451 452 // 6. Map the original base::SharedMemory and expect it contains the 453 // expected value. 454 ASSERT_TRUE(shared_memory.Map(123)); 455 EXPECT_EQ(kByeWorld, 456 std::string(static_cast<char*>(shared_memory.memory()))); 457 458 ASSERT_EQ(MOJO_RESULT_OK, MojoClose(sb1)); 459 END_CHILD() 460 } 461 462 enum class HandleType { 463 POSIX, 464 MACH, 465 MACH_NULL, 466 }; 467 468 const HandleType kTestHandleTypes[] = { 469 HandleType::MACH, 470 HandleType::MACH_NULL, 471 HandleType::POSIX, 472 HandleType::POSIX, 473 HandleType::MACH, 474 }; 475 476 // Test that we can mix file descriptors and mach port handles. 477 TEST_F(EmbedderTest, MultiprocessMixMachAndFds) { 478 const size_t kShmSize = 1234; 479 RUN_CHILD_ON_PIPE(MultiprocessMixMachAndFdsClient, server_mp) 480 // 1. Create fds or Mach objects and mojo handles from them. 481 MojoHandle platform_handles[arraysize(kTestHandleTypes)]; 482 for (size_t i = 0; i < arraysize(kTestHandleTypes); i++) { 483 const auto type = kTestHandleTypes[i]; 484 ScopedPlatformHandle scoped_handle; 485 if (type == HandleType::POSIX) { 486 // The easiest source of fds is opening /dev/null. 487 base::File file(base::FilePath("/dev/null"), 488 base::File::FLAG_OPEN | base::File::FLAG_WRITE); 489 ASSERT_TRUE(file.IsValid()); 490 scoped_handle.reset(PlatformHandle(file.TakePlatformFile())); 491 EXPECT_EQ(PlatformHandle::Type::POSIX, scoped_handle.get().type); 492 } else if (type == HandleType::MACH_NULL) { 493 scoped_handle.reset(PlatformHandle( 494 static_cast<mach_port_t>(MACH_PORT_NULL))); 495 EXPECT_EQ(PlatformHandle::Type::MACH, scoped_handle.get().type); 496 } else { 497 base::SharedMemoryCreateOptions options; 498 options.size = kShmSize; 499 base::SharedMemory shared_memory; 500 ASSERT_TRUE(shared_memory.Create(options)); 501 base::SharedMemoryHandle shm_handle = 502 base::SharedMemory::DuplicateHandle(shared_memory.handle()); 503 scoped_handle.reset(PlatformHandle(shm_handle.GetMemoryObject())); 504 EXPECT_EQ(PlatformHandle::Type::MACH, scoped_handle.get().type); 505 } 506 ASSERT_EQ(MOJO_RESULT_OK, CreatePlatformHandleWrapper( 507 std::move(scoped_handle), platform_handles + i)); 508 } 509 510 // 2. Send all the handles to the child. 511 WriteMessageWithHandles(server_mp, "hello", platform_handles, 512 arraysize(kTestHandleTypes)); 513 514 // 3. Read a message from |server_mp|. 515 EXPECT_EQ("bye", ReadMessage(server_mp)); 516 END_CHILD() 517 } 518 519 DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessMixMachAndFdsClient, EmbedderTest, 520 client_mp) { 521 const int kNumHandles = arraysize(kTestHandleTypes); 522 MojoHandle platform_handles[kNumHandles]; 523 524 // 1. Read from |client_mp|, which should have a message containing 525 // |kNumHandles| handles. 526 EXPECT_EQ("hello", 527 ReadMessageWithHandles(client_mp, platform_handles, kNumHandles)); 528 529 // 2. Extract each handle, and verify the type. 530 for (int i = 0; i < kNumHandles; i++) { 531 const auto type = kTestHandleTypes[i]; 532 ScopedPlatformHandle scoped_handle; 533 ASSERT_EQ(MOJO_RESULT_OK, 534 PassWrappedPlatformHandle(platform_handles[i], &scoped_handle)); 535 if (type == HandleType::POSIX) { 536 EXPECT_NE(0, scoped_handle.get().handle); 537 EXPECT_EQ(PlatformHandle::Type::POSIX, scoped_handle.get().type); 538 } else if (type == HandleType::MACH_NULL) { 539 EXPECT_EQ(static_cast<mach_port_t>(MACH_PORT_NULL), 540 scoped_handle.get().port); 541 EXPECT_EQ(PlatformHandle::Type::MACH, scoped_handle.get().type); 542 } else { 543 EXPECT_NE(static_cast<mach_port_t>(MACH_PORT_NULL), 544 scoped_handle.get().port); 545 EXPECT_EQ(PlatformHandle::Type::MACH, scoped_handle.get().type); 546 } 547 } 548 549 // 3. Say bye! 550 WriteMessage(client_mp, "bye"); 551 } 552 553 #endif // defined(OS_MACOSX) && !defined(OS_IOS) 554 555 // TODO(vtl): Test immediate write & close. 556 // TODO(vtl): Test broken-connection cases. 557 558 #endif // !defined(OS_IOS) 559 560 } // namespace 561 } // namespace edk 562 } // namespace mojo 563