Home | History | Annotate | Download | only in system
      1 // Copyright 2013 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 <stdint.h>
      6 #include <stdio.h>
      7 #include <string.h>
      8 
      9 #include <string>
     10 #include <vector>
     11 
     12 #include "base/bind.h"
     13 #include "base/files/file_path.h"
     14 #include "base/files/file_util.h"
     15 #include "base/files/scoped_file.h"
     16 #include "base/files/scoped_temp_dir.h"
     17 #include "base/location.h"
     18 #include "base/logging.h"
     19 #include "base/macros.h"
     20 #include "build/build_config.h"  // TODO(vtl): Remove this.
     21 #include "mojo/common/test/test_utils.h"
     22 #include "mojo/embedder/platform_shared_buffer.h"
     23 #include "mojo/embedder/scoped_platform_handle.h"
     24 #include "mojo/system/channel.h"
     25 #include "mojo/system/dispatcher.h"
     26 #include "mojo/system/message_pipe.h"
     27 #include "mojo/system/message_pipe_test_utils.h"
     28 #include "mojo/system/platform_handle_dispatcher.h"
     29 #include "mojo/system/raw_channel.h"
     30 #include "mojo/system/shared_buffer_dispatcher.h"
     31 #include "mojo/system/test_utils.h"
     32 #include "testing/gtest/include/gtest/gtest.h"
     33 
     34 namespace mojo {
     35 namespace system {
     36 namespace {
     37 
     38 class MultiprocessMessagePipeTest
     39     : public test::MultiprocessMessagePipeTestBase {};
     40 
     41 // For each message received, sends a reply message with the same contents
     42 // repeated twice, until the other end is closed or it receives "quitquitquit"
     43 // (which it doesn't reply to). It'll return the number of messages received,
     44 // not including any "quitquitquit" message, modulo 100.
     45 MOJO_MULTIPROCESS_TEST_CHILD_MAIN(EchoEcho) {
     46   embedder::SimplePlatformSupport platform_support;
     47   test::ChannelThread channel_thread(&platform_support);
     48   embedder::ScopedPlatformHandle client_platform_handle =
     49       mojo::test::MultiprocessTestHelper::client_platform_handle.Pass();
     50   CHECK(client_platform_handle.is_valid());
     51   scoped_refptr<ChannelEndpoint> ep;
     52   scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
     53   channel_thread.Start(client_platform_handle.Pass(), ep);
     54 
     55   const std::string quitquitquit("quitquitquit");
     56   int rv = 0;
     57   for (;; rv = (rv + 1) % 100) {
     58     // Wait for our end of the message pipe to be readable.
     59     HandleSignalsState hss;
     60     MojoResult result =
     61         test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss);
     62     if (result != MOJO_RESULT_OK) {
     63       // It was closed, probably.
     64       CHECK_EQ(result, MOJO_RESULT_FAILED_PRECONDITION);
     65       CHECK_EQ(hss.satisfied_signals, 0u);
     66       CHECK_EQ(hss.satisfiable_signals, 0u);
     67       break;
     68     } else {
     69       CHECK((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
     70       CHECK((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
     71     }
     72 
     73     std::string read_buffer(1000, '\0');
     74     uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size());
     75     CHECK_EQ(mp->ReadMessage(0,
     76                              UserPointer<void>(&read_buffer[0]),
     77                              MakeUserPointer(&read_buffer_size),
     78                              nullptr,
     79                              nullptr,
     80                              MOJO_READ_MESSAGE_FLAG_NONE),
     81              MOJO_RESULT_OK);
     82     read_buffer.resize(read_buffer_size);
     83     VLOG(2) << "Child got: " << read_buffer;
     84 
     85     if (read_buffer == quitquitquit) {
     86       VLOG(2) << "Child quitting.";
     87       break;
     88     }
     89 
     90     std::string write_buffer = read_buffer + read_buffer;
     91     CHECK_EQ(mp->WriteMessage(0,
     92                               UserPointer<const void>(write_buffer.data()),
     93                               static_cast<uint32_t>(write_buffer.size()),
     94                               nullptr,
     95                               MOJO_WRITE_MESSAGE_FLAG_NONE),
     96              MOJO_RESULT_OK);
     97   }
     98 
     99   mp->Close(0);
    100   return rv;
    101 }
    102 
    103 // Sends "hello" to child, and expects "hellohello" back.
    104 TEST_F(MultiprocessMessagePipeTest, Basic) {
    105   helper()->StartChild("EchoEcho");
    106 
    107   scoped_refptr<ChannelEndpoint> ep;
    108   scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
    109   Init(ep);
    110 
    111   std::string hello("hello");
    112   EXPECT_EQ(MOJO_RESULT_OK,
    113             mp->WriteMessage(0,
    114                              UserPointer<const void>(hello.data()),
    115                              static_cast<uint32_t>(hello.size()),
    116                              nullptr,
    117                              MOJO_WRITE_MESSAGE_FLAG_NONE));
    118 
    119   HandleSignalsState hss;
    120   EXPECT_EQ(MOJO_RESULT_OK,
    121             test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
    122   // The child may or may not have closed its end of the message pipe and died
    123   // (and we may or may not know it yet), so our end may or may not appear as
    124   // writable.
    125   EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
    126   EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
    127 
    128   std::string read_buffer(1000, '\0');
    129   uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size());
    130   CHECK_EQ(mp->ReadMessage(0,
    131                            UserPointer<void>(&read_buffer[0]),
    132                            MakeUserPointer(&read_buffer_size),
    133                            nullptr,
    134                            nullptr,
    135                            MOJO_READ_MESSAGE_FLAG_NONE),
    136            MOJO_RESULT_OK);
    137   read_buffer.resize(read_buffer_size);
    138   VLOG(2) << "Parent got: " << read_buffer;
    139   EXPECT_EQ(hello + hello, read_buffer);
    140 
    141   mp->Close(0);
    142 
    143   // We sent one message.
    144   EXPECT_EQ(1 % 100, helper()->WaitForChildShutdown());
    145 }
    146 
    147 // Sends a bunch of messages to the child. Expects them "repeated" back. Waits
    148 // for the child to close its end before quitting.
    149 TEST_F(MultiprocessMessagePipeTest, QueueMessages) {
    150   helper()->StartChild("EchoEcho");
    151 
    152   scoped_refptr<ChannelEndpoint> ep;
    153   scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
    154   Init(ep);
    155 
    156   static const size_t kNumMessages = 1001;
    157   for (size_t i = 0; i < kNumMessages; i++) {
    158     std::string write_buffer(i, 'A' + (i % 26));
    159     EXPECT_EQ(MOJO_RESULT_OK,
    160               mp->WriteMessage(0,
    161                                UserPointer<const void>(write_buffer.data()),
    162                                static_cast<uint32_t>(write_buffer.size()),
    163                                nullptr,
    164                                MOJO_WRITE_MESSAGE_FLAG_NONE));
    165   }
    166 
    167   const std::string quitquitquit("quitquitquit");
    168   EXPECT_EQ(MOJO_RESULT_OK,
    169             mp->WriteMessage(0,
    170                              UserPointer<const void>(quitquitquit.data()),
    171                              static_cast<uint32_t>(quitquitquit.size()),
    172                              nullptr,
    173                              MOJO_WRITE_MESSAGE_FLAG_NONE));
    174 
    175   for (size_t i = 0; i < kNumMessages; i++) {
    176     HandleSignalsState hss;
    177     EXPECT_EQ(MOJO_RESULT_OK,
    178               test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
    179     // The child may or may not have closed its end of the message pipe and died
    180     // (and we may or may not know it yet), so our end may or may not appear as
    181     // writable.
    182     EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
    183     EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
    184 
    185     std::string read_buffer(kNumMessages * 2, '\0');
    186     uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size());
    187     CHECK_EQ(mp->ReadMessage(0,
    188                              UserPointer<void>(&read_buffer[0]),
    189                              MakeUserPointer(&read_buffer_size),
    190                              nullptr,
    191                              nullptr,
    192                              MOJO_READ_MESSAGE_FLAG_NONE),
    193              MOJO_RESULT_OK);
    194     read_buffer.resize(read_buffer_size);
    195 
    196     EXPECT_EQ(std::string(i * 2, 'A' + (i % 26)), read_buffer);
    197   }
    198 
    199   // Wait for it to become readable, which should fail (since we sent
    200   // "quitquitquit").
    201   HandleSignalsState hss;
    202   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
    203             test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
    204   EXPECT_EQ(0u, hss.satisfied_signals);
    205   EXPECT_EQ(0u, hss.satisfiable_signals);
    206 
    207   mp->Close(0);
    208 
    209   EXPECT_EQ(static_cast<int>(kNumMessages % 100),
    210             helper()->WaitForChildShutdown());
    211 }
    212 
    213 MOJO_MULTIPROCESS_TEST_CHILD_MAIN(CheckSharedBuffer) {
    214   embedder::SimplePlatformSupport platform_support;
    215   test::ChannelThread channel_thread(&platform_support);
    216   embedder::ScopedPlatformHandle client_platform_handle =
    217       mojo::test::MultiprocessTestHelper::client_platform_handle.Pass();
    218   CHECK(client_platform_handle.is_valid());
    219   scoped_refptr<ChannelEndpoint> ep;
    220   scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
    221   channel_thread.Start(client_platform_handle.Pass(), ep);
    222 
    223   // Wait for the first message from our parent.
    224   HandleSignalsState hss;
    225   CHECK_EQ(test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss),
    226            MOJO_RESULT_OK);
    227   // In this test, the parent definitely doesn't close its end of the message
    228   // pipe before we do.
    229   CHECK_EQ(hss.satisfied_signals,
    230            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
    231   CHECK_EQ(hss.satisfiable_signals,
    232            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
    233 
    234   // It should have a shared buffer.
    235   std::string read_buffer(100, '\0');
    236   uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size());
    237   DispatcherVector dispatchers;
    238   uint32_t num_dispatchers = 10;  // Maximum number to receive.
    239   CHECK_EQ(mp->ReadMessage(0,
    240                            UserPointer<void>(&read_buffer[0]),
    241                            MakeUserPointer(&num_bytes),
    242                            &dispatchers,
    243                            &num_dispatchers,
    244                            MOJO_READ_MESSAGE_FLAG_NONE),
    245            MOJO_RESULT_OK);
    246   read_buffer.resize(num_bytes);
    247   CHECK_EQ(read_buffer, std::string("go 1"));
    248   CHECK_EQ(num_dispatchers, 1u);
    249 
    250   CHECK_EQ(dispatchers[0]->GetType(), Dispatcher::kTypeSharedBuffer);
    251 
    252   scoped_refptr<SharedBufferDispatcher> dispatcher(
    253       static_cast<SharedBufferDispatcher*>(dispatchers[0].get()));
    254 
    255   // Make a mapping.
    256   scoped_ptr<embedder::PlatformSharedBufferMapping> mapping;
    257   CHECK_EQ(dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping),
    258            MOJO_RESULT_OK);
    259   CHECK(mapping);
    260   CHECK(mapping->GetBase());
    261   CHECK_EQ(mapping->GetLength(), 100u);
    262 
    263   // Write some stuff to the shared buffer.
    264   static const char kHello[] = "hello";
    265   memcpy(mapping->GetBase(), kHello, sizeof(kHello));
    266 
    267   // We should be able to close the dispatcher now.
    268   dispatcher->Close();
    269 
    270   // And send a message to signal that we've written stuff.
    271   const std::string go2("go 2");
    272   CHECK_EQ(mp->WriteMessage(0,
    273                             UserPointer<const void>(&go2[0]),
    274                             static_cast<uint32_t>(go2.size()),
    275                             nullptr,
    276                             MOJO_WRITE_MESSAGE_FLAG_NONE),
    277            MOJO_RESULT_OK);
    278 
    279   // Now wait for our parent to send us a message.
    280   hss = HandleSignalsState();
    281   CHECK_EQ(test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss),
    282            MOJO_RESULT_OK);
    283   CHECK_EQ(hss.satisfied_signals,
    284            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
    285   CHECK_EQ(hss.satisfiable_signals,
    286            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
    287 
    288   read_buffer = std::string(100, '\0');
    289   num_bytes = static_cast<uint32_t>(read_buffer.size());
    290   CHECK_EQ(mp->ReadMessage(0,
    291                            UserPointer<void>(&read_buffer[0]),
    292                            MakeUserPointer(&num_bytes),
    293                            nullptr,
    294                            nullptr,
    295                            MOJO_READ_MESSAGE_FLAG_NONE),
    296            MOJO_RESULT_OK);
    297   read_buffer.resize(num_bytes);
    298   CHECK_EQ(read_buffer, std::string("go 3"));
    299 
    300   // It should have written something to the shared buffer.
    301   static const char kWorld[] = "world!!!";
    302   CHECK_EQ(memcmp(mapping->GetBase(), kWorld, sizeof(kWorld)), 0);
    303 
    304   // And we're done.
    305   mp->Close(0);
    306 
    307   return 0;
    308 }
    309 
    310 #if defined(OS_POSIX)
    311 #define MAYBE_SharedBufferPassing SharedBufferPassing
    312 #else
    313 // Not yet implemented (on Windows).
    314 #define MAYBE_SharedBufferPassing DISABLED_SharedBufferPassing
    315 #endif
    316 TEST_F(MultiprocessMessagePipeTest, MAYBE_SharedBufferPassing) {
    317   helper()->StartChild("CheckSharedBuffer");
    318 
    319   scoped_refptr<ChannelEndpoint> ep;
    320   scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
    321   Init(ep);
    322 
    323   // Make a shared buffer.
    324   scoped_refptr<SharedBufferDispatcher> dispatcher;
    325   EXPECT_EQ(MOJO_RESULT_OK,
    326             SharedBufferDispatcher::Create(
    327                 platform_support(),
    328                 SharedBufferDispatcher::kDefaultCreateOptions,
    329                 100,
    330                 &dispatcher));
    331   ASSERT_TRUE(dispatcher.get());
    332 
    333   // Make a mapping.
    334   scoped_ptr<embedder::PlatformSharedBufferMapping> mapping;
    335   EXPECT_EQ(MOJO_RESULT_OK,
    336             dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping));
    337   ASSERT_TRUE(mapping);
    338   ASSERT_TRUE(mapping->GetBase());
    339   ASSERT_EQ(100u, mapping->GetLength());
    340 
    341   // Send the shared buffer.
    342   const std::string go1("go 1");
    343   DispatcherTransport transport(
    344       test::DispatcherTryStartTransport(dispatcher.get()));
    345   ASSERT_TRUE(transport.is_valid());
    346 
    347   std::vector<DispatcherTransport> transports;
    348   transports.push_back(transport);
    349   EXPECT_EQ(MOJO_RESULT_OK,
    350             mp->WriteMessage(0,
    351                              UserPointer<const void>(&go1[0]),
    352                              static_cast<uint32_t>(go1.size()),
    353                              &transports,
    354                              MOJO_WRITE_MESSAGE_FLAG_NONE));
    355   transport.End();
    356 
    357   EXPECT_TRUE(dispatcher->HasOneRef());
    358   dispatcher = nullptr;
    359 
    360   // Wait for a message from the child.
    361   HandleSignalsState hss;
    362   EXPECT_EQ(MOJO_RESULT_OK,
    363             test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
    364   EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
    365   EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
    366 
    367   std::string read_buffer(100, '\0');
    368   uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size());
    369   EXPECT_EQ(MOJO_RESULT_OK,
    370             mp->ReadMessage(0,
    371                             UserPointer<void>(&read_buffer[0]),
    372                             MakeUserPointer(&num_bytes),
    373                             nullptr,
    374                             nullptr,
    375                             MOJO_READ_MESSAGE_FLAG_NONE));
    376   read_buffer.resize(num_bytes);
    377   EXPECT_EQ(std::string("go 2"), read_buffer);
    378 
    379   // After we get it, the child should have written something to the shared
    380   // buffer.
    381   static const char kHello[] = "hello";
    382   EXPECT_EQ(0, memcmp(mapping->GetBase(), kHello, sizeof(kHello)));
    383 
    384   // Now we'll write some stuff to the shared buffer.
    385   static const char kWorld[] = "world!!!";
    386   memcpy(mapping->GetBase(), kWorld, sizeof(kWorld));
    387 
    388   // And send a message to signal that we've written stuff.
    389   const std::string go3("go 3");
    390   EXPECT_EQ(MOJO_RESULT_OK,
    391             mp->WriteMessage(0,
    392                              UserPointer<const void>(&go3[0]),
    393                              static_cast<uint32_t>(go3.size()),
    394                              nullptr,
    395                              MOJO_WRITE_MESSAGE_FLAG_NONE));
    396 
    397   // Wait for |mp| to become readable, which should fail.
    398   hss = HandleSignalsState();
    399   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
    400             test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
    401   EXPECT_EQ(0u, hss.satisfied_signals);
    402   EXPECT_EQ(0u, hss.satisfiable_signals);
    403 
    404   mp->Close(0);
    405 
    406   EXPECT_EQ(0, helper()->WaitForChildShutdown());
    407 }
    408 
    409 MOJO_MULTIPROCESS_TEST_CHILD_MAIN(CheckPlatformHandleFile) {
    410   embedder::SimplePlatformSupport platform_support;
    411   test::ChannelThread channel_thread(&platform_support);
    412   embedder::ScopedPlatformHandle client_platform_handle =
    413       mojo::test::MultiprocessTestHelper::client_platform_handle.Pass();
    414   CHECK(client_platform_handle.is_valid());
    415   scoped_refptr<ChannelEndpoint> ep;
    416   scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
    417   channel_thread.Start(client_platform_handle.Pass(), ep);
    418 
    419   HandleSignalsState hss;
    420   CHECK_EQ(test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss),
    421            MOJO_RESULT_OK);
    422   CHECK_EQ(hss.satisfied_signals,
    423            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
    424   CHECK_EQ(hss.satisfiable_signals,
    425            MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
    426 
    427   std::string read_buffer(100, '\0');
    428   uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size());
    429   DispatcherVector dispatchers;
    430   uint32_t num_dispatchers = 10;  // Maximum number to receive.
    431   CHECK_EQ(mp->ReadMessage(0,
    432                            UserPointer<void>(&read_buffer[0]),
    433                            MakeUserPointer(&num_bytes),
    434                            &dispatchers,
    435                            &num_dispatchers,
    436                            MOJO_READ_MESSAGE_FLAG_NONE),
    437            MOJO_RESULT_OK);
    438   mp->Close(0);
    439 
    440   read_buffer.resize(num_bytes);
    441   CHECK_EQ(read_buffer, std::string("hello"));
    442   CHECK_EQ(num_dispatchers, 1u);
    443 
    444   CHECK_EQ(dispatchers[0]->GetType(), Dispatcher::kTypePlatformHandle);
    445 
    446   scoped_refptr<PlatformHandleDispatcher> dispatcher(
    447       static_cast<PlatformHandleDispatcher*>(dispatchers[0].get()));
    448   embedder::ScopedPlatformHandle h = dispatcher->PassPlatformHandle().Pass();
    449   CHECK(h.is_valid());
    450   dispatcher->Close();
    451 
    452   base::ScopedFILE fp(mojo::test::FILEFromPlatformHandle(h.Pass(), "r"));
    453   CHECK(fp);
    454   std::string fread_buffer(100, '\0');
    455   size_t bytes_read = fread(&fread_buffer[0], 1, fread_buffer.size(), fp.get());
    456   fread_buffer.resize(bytes_read);
    457   CHECK_EQ(fread_buffer, "world");
    458 
    459   return 0;
    460 }
    461 
    462 #if defined(OS_POSIX)
    463 #define MAYBE_PlatformHandlePassing PlatformHandlePassing
    464 #else
    465 // Not yet implemented (on Windows).
    466 #define MAYBE_PlatformHandlePassing DISABLED_PlatformHandlePassing
    467 #endif
    468 TEST_F(MultiprocessMessagePipeTest, MAYBE_PlatformHandlePassing) {
    469   base::ScopedTempDir temp_dir;
    470   ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
    471 
    472   helper()->StartChild("CheckPlatformHandleFile");
    473 
    474   scoped_refptr<ChannelEndpoint> ep;
    475   scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
    476   Init(ep);
    477 
    478   base::FilePath unused;
    479   base::ScopedFILE fp(
    480       CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
    481   const std::string world("world");
    482   ASSERT_EQ(fwrite(&world[0], 1, world.size(), fp.get()), world.size());
    483   fflush(fp.get());
    484   rewind(fp.get());
    485 
    486   embedder::ScopedPlatformHandle h(
    487       mojo::test::PlatformHandleFromFILE(fp.Pass()));
    488   scoped_refptr<PlatformHandleDispatcher> dispatcher(
    489       new PlatformHandleDispatcher(h.Pass()));
    490 
    491   const std::string hello("hello");
    492   DispatcherTransport transport(
    493       test::DispatcherTryStartTransport(dispatcher.get()));
    494   ASSERT_TRUE(transport.is_valid());
    495 
    496   std::vector<DispatcherTransport> transports;
    497   transports.push_back(transport);
    498   EXPECT_EQ(MOJO_RESULT_OK,
    499             mp->WriteMessage(0,
    500                              UserPointer<const void>(&hello[0]),
    501                              static_cast<uint32_t>(hello.size()),
    502                              &transports,
    503                              MOJO_WRITE_MESSAGE_FLAG_NONE));
    504   transport.End();
    505 
    506   EXPECT_TRUE(dispatcher->HasOneRef());
    507   dispatcher = nullptr;
    508 
    509   // Wait for it to become readable, which should fail.
    510   HandleSignalsState hss;
    511   EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
    512             test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
    513   EXPECT_EQ(0u, hss.satisfied_signals);
    514   EXPECT_EQ(0u, hss.satisfiable_signals);
    515 
    516   mp->Close(0);
    517 
    518   EXPECT_EQ(0, helper()->WaitForChildShutdown());
    519 }
    520 
    521 }  // namespace
    522 }  // namespace system
    523 }  // namespace mojo
    524