Home | History | Annotate | Download | only in embedder
      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/embedder/embedder.h"
      6 
      7 #include <string.h>
      8 
      9 #include "base/bind.h"
     10 #include "base/location.h"
     11 #include "base/logging.h"
     12 #include "base/macros.h"
     13 #include "base/message_loop/message_loop.h"
     14 #include "base/synchronization/waitable_event.h"
     15 #include "mojo/common/test/multiprocess_test_helper.h"
     16 #include "mojo/embedder/platform_channel_pair.h"
     17 #include "mojo/embedder/test_embedder.h"
     18 #include "mojo/public/c/system/core.h"
     19 #include "mojo/system/test_utils.h"
     20 #include "testing/gtest/include/gtest/gtest.h"
     21 
     22 namespace mojo {
     23 namespace embedder {
     24 namespace {
     25 
     26 class ScopedTestChannel {
     27  public:
     28   // Creates a channel that lives on a given I/O thread (determined by the given
     29   // |TaskRunner|) attached to the given |platform_handle|. After construction,
     30   // |bootstrap_message_pipe()| gives the Mojo handle for the bootstrap message
     31   // pipe on this channel; it is up to the caller to close this handle.
     32   // Note: The I/O thread must outlive this object (and its message loop must
     33   // continue pumping messages while this object is alive).
     34   ScopedTestChannel(scoped_refptr<base::TaskRunner> io_thread_task_runner,
     35                     ScopedPlatformHandle platform_handle)
     36       : io_thread_task_runner_(io_thread_task_runner),
     37         bootstrap_message_pipe_(MOJO_HANDLE_INVALID),
     38         did_create_channel_event_(true, false),
     39         channel_info_(NULL) {
     40     bootstrap_message_pipe_ = CreateChannel(
     41         platform_handle.Pass(), io_thread_task_runner_,
     42         base::Bind(&ScopedTestChannel::DidCreateChannel,
     43                    base::Unretained(this)), NULL).release().value();
     44     CHECK_NE(bootstrap_message_pipe_, MOJO_HANDLE_INVALID);
     45   }
     46 
     47   // Destructor: Shuts down the channel. (As noted above, for this to happen,
     48   // the I/O thread must be alive and pumping messages.)
     49   ~ScopedTestChannel() {
     50     system::test::PostTaskAndWait(
     51         io_thread_task_runner_,
     52         FROM_HERE,
     53         base::Bind(&ScopedTestChannel::DestroyChannel, base::Unretained(this)));
     54   }
     55 
     56   // Waits for channel creation to be completed.
     57   void WaitForChannelCreationCompletion() {
     58     did_create_channel_event_.Wait();
     59   }
     60 
     61   MojoHandle bootstrap_message_pipe() const { return bootstrap_message_pipe_; }
     62 
     63   // Call only after |WaitForChannelCreationCompletion()|. Use only to check
     64   // that it's not null.
     65   const ChannelInfo* channel_info() const { return channel_info_; }
     66 
     67  private:
     68   void DidCreateChannel(ChannelInfo* channel_info) {
     69     CHECK(channel_info);
     70     CHECK(!channel_info_);
     71     channel_info_ = channel_info;
     72     did_create_channel_event_.Signal();
     73   }
     74 
     75   void DestroyChannel() {
     76     CHECK(channel_info_);
     77     DestroyChannelOnIOThread(channel_info_);
     78     channel_info_ = NULL;
     79   }
     80 
     81   scoped_refptr<base::TaskRunner> io_thread_task_runner_;
     82 
     83   // Valid from creation until whenever it gets closed (by the "owner" of this
     84   // object).
     85   // Note: We don't want use the C++ wrappers here, since we want to test the
     86   // API at the lowest level.
     87   MojoHandle bootstrap_message_pipe_;
     88 
     89   // Set after channel creation has been completed (i.e., the callback to
     90   // |CreateChannel()| has been called).
     91   base::WaitableEvent did_create_channel_event_;
     92 
     93   // Valid after channel creation completion until destruction.
     94   ChannelInfo* channel_info_;
     95 
     96   DISALLOW_COPY_AND_ASSIGN(ScopedTestChannel);
     97 };
     98 
     99 class EmbedderTest : public testing::Test {
    100  public:
    101   EmbedderTest() : test_io_thread_(system::test::TestIOThread::kAutoStart) {}
    102   virtual ~EmbedderTest() {}
    103 
    104  protected:
    105   system::test::TestIOThread* test_io_thread() { return &test_io_thread_; }
    106 
    107  private:
    108   system::test::TestIOThread test_io_thread_;
    109 
    110   DISALLOW_COPY_AND_ASSIGN(EmbedderTest);
    111 };
    112 
    113 TEST_F(EmbedderTest, ChannelsBasic) {
    114   Init();
    115 
    116   {
    117     PlatformChannelPair channel_pair;
    118     ScopedTestChannel server_channel(test_io_thread()->task_runner(),
    119                                      channel_pair.PassServerHandle());
    120     MojoHandle server_mp = server_channel.bootstrap_message_pipe();
    121     EXPECT_NE(server_mp, MOJO_HANDLE_INVALID);
    122     ScopedTestChannel client_channel(test_io_thread()->task_runner(),
    123                                      channel_pair.PassClientHandle());
    124     MojoHandle client_mp = client_channel.bootstrap_message_pipe();
    125     EXPECT_NE(client_mp, MOJO_HANDLE_INVALID);
    126 
    127     // We can write to a message pipe handle immediately.
    128     const char kHello[] = "hello";
    129     EXPECT_EQ(MOJO_RESULT_OK,
    130               MojoWriteMessage(server_mp, kHello,
    131                                static_cast<uint32_t>(sizeof(kHello)), NULL, 0,
    132                                MOJO_WRITE_MESSAGE_FLAG_NONE));
    133 
    134     // Now wait for the other side to become readable.
    135     EXPECT_EQ(MOJO_RESULT_OK,
    136               MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE,
    137                        MOJO_DEADLINE_INDEFINITE));
    138 
    139     char buffer[1000] = {};
    140     uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
    141     EXPECT_EQ(MOJO_RESULT_OK,
    142               MojoReadMessage(client_mp, buffer, &num_bytes, NULL, NULL,
    143                               MOJO_READ_MESSAGE_FLAG_NONE));
    144     EXPECT_EQ(sizeof(kHello), num_bytes);
    145     EXPECT_STREQ(kHello, buffer);
    146 
    147     EXPECT_EQ(MOJO_RESULT_OK, MojoClose(server_mp));
    148     EXPECT_EQ(MOJO_RESULT_OK, MojoClose(client_mp));
    149 
    150     // By this point, these waits should basically be no-ops (since we've waited
    151     // for the client message pipe to become readable, which implies that both
    152     // the server and client channels were completely created).
    153     server_channel.WaitForChannelCreationCompletion();
    154     client_channel.WaitForChannelCreationCompletion();
    155     EXPECT_TRUE(server_channel.channel_info() != NULL);
    156     EXPECT_TRUE(client_channel.channel_info() != NULL);
    157   }
    158 
    159   EXPECT_TRUE(test::Shutdown());
    160 }
    161 
    162 TEST_F(EmbedderTest, ChannelsHandlePassing) {
    163   Init();
    164 
    165   {
    166     PlatformChannelPair channel_pair;
    167     ScopedTestChannel server_channel(test_io_thread()->task_runner(),
    168                                      channel_pair.PassServerHandle());
    169     MojoHandle server_mp = server_channel.bootstrap_message_pipe();
    170     EXPECT_NE(server_mp, MOJO_HANDLE_INVALID);
    171     ScopedTestChannel client_channel(test_io_thread()->task_runner(),
    172                                      channel_pair.PassClientHandle());
    173     MojoHandle client_mp = client_channel.bootstrap_message_pipe();
    174     EXPECT_NE(client_mp, MOJO_HANDLE_INVALID);
    175 
    176     MojoHandle h0, h1;
    177     EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(NULL, &h0, &h1));
    178 
    179     // Write a message to |h0| (attaching nothing).
    180     const char kHello[] = "hello";
    181     EXPECT_EQ(MOJO_RESULT_OK,
    182               MojoWriteMessage(h0, kHello,
    183                                static_cast<uint32_t>(sizeof(kHello)), NULL, 0,
    184                                MOJO_WRITE_MESSAGE_FLAG_NONE));
    185 
    186     // Write one message to |server_mp|, attaching |h1|.
    187     const char kWorld[] = "world!!!";
    188     EXPECT_EQ(MOJO_RESULT_OK,
    189               MojoWriteMessage(server_mp, kWorld,
    190                                static_cast<uint32_t>(sizeof(kWorld)), &h1, 1,
    191                                MOJO_WRITE_MESSAGE_FLAG_NONE));
    192     h1 = MOJO_HANDLE_INVALID;
    193 
    194     // Write another message to |h0|.
    195     const char kFoo[] = "foo";
    196     EXPECT_EQ(MOJO_RESULT_OK,
    197               MojoWriteMessage(h0, kFoo,
    198                                static_cast<uint32_t>(sizeof(kFoo)), NULL, 0,
    199                                MOJO_WRITE_MESSAGE_FLAG_NONE));
    200 
    201     // Wait for |client_mp| to become readable.
    202     EXPECT_EQ(MOJO_RESULT_OK,
    203               MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE,
    204                        MOJO_DEADLINE_INDEFINITE));
    205 
    206     // Read a message from |client_mp|.
    207     char buffer[1000] = {};
    208     uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
    209     MojoHandle handles[10] = {};
    210     uint32_t num_handles = arraysize(handles);
    211     EXPECT_EQ(MOJO_RESULT_OK,
    212               MojoReadMessage(client_mp, buffer, &num_bytes, handles,
    213                               &num_handles, MOJO_READ_MESSAGE_FLAG_NONE));
    214     EXPECT_EQ(sizeof(kWorld), num_bytes);
    215     EXPECT_STREQ(kWorld, buffer);
    216     EXPECT_EQ(1u, num_handles);
    217     EXPECT_NE(handles[0], MOJO_HANDLE_INVALID);
    218     h1 = handles[0];
    219 
    220     // Wait for |h1| to become readable.
    221     EXPECT_EQ(MOJO_RESULT_OK,
    222               MojoWait(h1, MOJO_HANDLE_SIGNAL_READABLE,
    223                        MOJO_DEADLINE_INDEFINITE));
    224 
    225     // Read a message from |h1|.
    226     memset(buffer, 0, sizeof(buffer));
    227     num_bytes = static_cast<uint32_t>(sizeof(buffer));
    228     memset(handles, 0, sizeof(handles));
    229     num_handles = arraysize(handles);
    230     EXPECT_EQ(MOJO_RESULT_OK,
    231               MojoReadMessage(h1, buffer, &num_bytes, handles, &num_handles,
    232                               MOJO_READ_MESSAGE_FLAG_NONE));
    233     EXPECT_EQ(sizeof(kHello), num_bytes);
    234     EXPECT_STREQ(kHello, buffer);
    235     EXPECT_EQ(0u, num_handles);
    236 
    237     // Wait for |h1| to become readable (again).
    238     EXPECT_EQ(MOJO_RESULT_OK,
    239               MojoWait(h1, MOJO_HANDLE_SIGNAL_READABLE,
    240                        MOJO_DEADLINE_INDEFINITE));
    241 
    242     // Read the second message from |h1|.
    243     memset(buffer, 0, sizeof(buffer));
    244     num_bytes = static_cast<uint32_t>(sizeof(buffer));
    245     EXPECT_EQ(MOJO_RESULT_OK,
    246               MojoReadMessage(h1, buffer, &num_bytes, NULL, NULL,
    247                               MOJO_READ_MESSAGE_FLAG_NONE));
    248     EXPECT_EQ(sizeof(kFoo), num_bytes);
    249     EXPECT_STREQ(kFoo, buffer);
    250 
    251     // Write a message to |h1|.
    252     const char kBarBaz[] = "barbaz";
    253     EXPECT_EQ(MOJO_RESULT_OK,
    254               MojoWriteMessage(h1, kBarBaz,
    255                                static_cast<uint32_t>(sizeof(kBarBaz)), NULL, 0,
    256                                MOJO_WRITE_MESSAGE_FLAG_NONE));
    257 
    258     // Wait for |h0| to become readable.
    259     EXPECT_EQ(MOJO_RESULT_OK,
    260               MojoWait(h0, MOJO_HANDLE_SIGNAL_READABLE,
    261                        MOJO_DEADLINE_INDEFINITE));
    262 
    263     // Read a message from |h0|.
    264     memset(buffer, 0, sizeof(buffer));
    265     num_bytes = static_cast<uint32_t>(sizeof(buffer));
    266     EXPECT_EQ(MOJO_RESULT_OK,
    267               MojoReadMessage(h0, buffer, &num_bytes, NULL, NULL,
    268                               MOJO_READ_MESSAGE_FLAG_NONE));
    269     EXPECT_EQ(sizeof(kBarBaz), num_bytes);
    270     EXPECT_STREQ(kBarBaz, buffer);
    271 
    272     EXPECT_EQ(MOJO_RESULT_OK, MojoClose(server_mp));
    273     EXPECT_EQ(MOJO_RESULT_OK, MojoClose(client_mp));
    274     EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h0));
    275     EXPECT_EQ(MOJO_RESULT_OK, MojoClose(h1));
    276 
    277     server_channel.WaitForChannelCreationCompletion();
    278     client_channel.WaitForChannelCreationCompletion();
    279     EXPECT_TRUE(server_channel.channel_info() != NULL);
    280     EXPECT_TRUE(client_channel.channel_info() != NULL);
    281   }
    282 
    283   EXPECT_TRUE(test::Shutdown());
    284 }
    285 
    286 // The sequence of messages sent is:
    287 //       server_mp   client_mp   mp0         mp1         mp2         mp3
    288 //   1.  "hello"
    289 //   2.              "world!"
    290 //   3.                          "FOO"
    291 //   4.  "Bar"+mp1
    292 //   5.  (close)
    293 //   6.              (close)
    294 //   7.                                                              "baz"
    295 //   8.                                                              (closed)
    296 //   9.                                      "quux"+mp2
    297 //  10.                          (close)
    298 //  11.                                      (wait/cl.)
    299 //  12.                                                  (wait/cl.)
    300 TEST_F(EmbedderTest, MultiprocessChannels) {
    301   Init();
    302   mojo::test::MultiprocessTestHelper multiprocess_test_helper;
    303   multiprocess_test_helper.StartChild("MultiprocessChannelsClient");
    304 
    305   {
    306     ScopedTestChannel server_channel(
    307         test_io_thread()->task_runner(),
    308         multiprocess_test_helper.server_platform_handle.Pass());
    309     MojoHandle server_mp = server_channel.bootstrap_message_pipe();
    310     EXPECT_NE(server_mp, MOJO_HANDLE_INVALID);
    311     server_channel.WaitForChannelCreationCompletion();
    312     EXPECT_TRUE(server_channel.channel_info() != NULL);
    313 
    314     // 1. Write a message to |server_mp| (attaching nothing).
    315     const char kHello[] = "hello";
    316     EXPECT_EQ(MOJO_RESULT_OK,
    317               MojoWriteMessage(server_mp, kHello,
    318                                static_cast<uint32_t>(sizeof(kHello)), NULL, 0,
    319                                MOJO_WRITE_MESSAGE_FLAG_NONE));
    320 
    321     // TODO(vtl): If the scope were ended immediately here (maybe after closing
    322     // |server_mp|), we die with a fatal error in |Channel::HandleLocalError()|.
    323 
    324     // 2. Read a message from |server_mp|.
    325     EXPECT_EQ(MOJO_RESULT_OK,
    326               MojoWait(server_mp, MOJO_HANDLE_SIGNAL_READABLE,
    327                        MOJO_DEADLINE_INDEFINITE));
    328     char buffer[1000] = {};
    329     uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
    330     EXPECT_EQ(MOJO_RESULT_OK,
    331               MojoReadMessage(server_mp, buffer, &num_bytes, NULL, NULL,
    332                               MOJO_READ_MESSAGE_FLAG_NONE));
    333     const char kWorld[] = "world!";
    334     EXPECT_EQ(sizeof(kWorld), num_bytes);
    335     EXPECT_STREQ(kWorld, buffer);
    336 
    337     // Create a new message pipe (endpoints |mp0| and |mp1|).
    338     MojoHandle mp0, mp1;
    339     EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(NULL, &mp0, &mp1));
    340 
    341     // 3. Write something to |mp0|.
    342     const char kFoo[] = "FOO";
    343     EXPECT_EQ(MOJO_RESULT_OK,
    344               MojoWriteMessage(mp0, kFoo,
    345                                static_cast<uint32_t>(sizeof(kFoo)), NULL, 0,
    346                                MOJO_WRITE_MESSAGE_FLAG_NONE));
    347 
    348     // 4. Write a message to |server_mp|, attaching |mp1|.
    349     const char kBar[] = "Bar";
    350     EXPECT_EQ(MOJO_RESULT_OK,
    351               MojoWriteMessage(server_mp, kBar,
    352                                static_cast<uint32_t>(sizeof(kBar)), &mp1, 1,
    353                                MOJO_WRITE_MESSAGE_FLAG_NONE));
    354     mp1 = MOJO_HANDLE_INVALID;
    355 
    356     // 5. Close |server_mp|.
    357     EXPECT_EQ(MOJO_RESULT_OK, MojoClose(server_mp));
    358 
    359     // 9. Read a message from |mp0|, which should have |mp2| attached.
    360     EXPECT_EQ(MOJO_RESULT_OK,
    361               MojoWait(mp0, MOJO_HANDLE_SIGNAL_READABLE,
    362                        MOJO_DEADLINE_INDEFINITE));
    363     memset(buffer, 0, sizeof(buffer));
    364     num_bytes = static_cast<uint32_t>(sizeof(buffer));
    365     MojoHandle mp2 = MOJO_HANDLE_INVALID;
    366     uint32_t num_handles = 1;
    367     EXPECT_EQ(MOJO_RESULT_OK,
    368               MojoReadMessage(mp0, buffer, &num_bytes, &mp2, &num_handles,
    369                               MOJO_READ_MESSAGE_FLAG_NONE));
    370     const char kQuux[] = "quux";
    371     EXPECT_EQ(sizeof(kQuux), num_bytes);
    372     EXPECT_STREQ(kQuux, buffer);
    373     EXPECT_EQ(1u, num_handles);
    374     EXPECT_NE(mp2, MOJO_HANDLE_INVALID);
    375 
    376     // 7. Read a message from |mp2|.
    377     EXPECT_EQ(MOJO_RESULT_OK,
    378               MojoWait(mp2, MOJO_HANDLE_SIGNAL_READABLE,
    379                        MOJO_DEADLINE_INDEFINITE));
    380     memset(buffer, 0, sizeof(buffer));
    381     num_bytes = static_cast<uint32_t>(sizeof(buffer));
    382     EXPECT_EQ(MOJO_RESULT_OK,
    383               MojoReadMessage(mp2, buffer, &num_bytes, NULL, NULL,
    384                               MOJO_READ_MESSAGE_FLAG_NONE));
    385     const char kBaz[] = "baz";
    386     EXPECT_EQ(sizeof(kBaz), num_bytes);
    387     EXPECT_STREQ(kBaz, buffer);
    388 
    389     // 10. Close |mp0|.
    390     EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mp0));
    391 
    392     // 12. Wait on |mp2| (which should eventually fail) and then close it.
    393 // TODO(vtl): crbug.com/351768
    394 #if 0
    395     EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
    396               MojoWait(mp2, MOJO_HANDLE_SIGNAL_READABLE,
    397                        MOJO_DEADLINE_INDEFINITE));
    398 #endif
    399     EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mp2));
    400   }
    401 
    402   EXPECT_TRUE(multiprocess_test_helper.WaitForChildTestShutdown());
    403   EXPECT_TRUE(test::Shutdown());
    404 }
    405 
    406 MOJO_MULTIPROCESS_TEST_CHILD_TEST(MultiprocessChannelsClient) {
    407   embedder::ScopedPlatformHandle client_platform_handle =
    408       mojo::test::MultiprocessTestHelper::client_platform_handle.Pass();
    409   EXPECT_TRUE(client_platform_handle.is_valid());
    410 
    411   system::test::TestIOThread
    412       test_io_thread(system::test::TestIOThread::kAutoStart);
    413   Init();
    414 
    415   {
    416     ScopedTestChannel client_channel(test_io_thread.task_runner(),
    417                                      client_platform_handle.Pass());
    418     MojoHandle client_mp = client_channel.bootstrap_message_pipe();
    419     EXPECT_NE(client_mp, MOJO_HANDLE_INVALID);
    420     client_channel.WaitForChannelCreationCompletion();
    421     CHECK(client_channel.channel_info() != NULL);
    422 
    423     // 1. Read the first message from |client_mp|.
    424     EXPECT_EQ(MOJO_RESULT_OK,
    425               MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE,
    426                        MOJO_DEADLINE_INDEFINITE));
    427     char buffer[1000] = {};
    428     uint32_t num_bytes = static_cast<uint32_t>(sizeof(buffer));
    429     EXPECT_EQ(MOJO_RESULT_OK,
    430               MojoReadMessage(client_mp, buffer, &num_bytes, NULL, NULL,
    431                               MOJO_READ_MESSAGE_FLAG_NONE));
    432     const char kHello[] = "hello";
    433     EXPECT_EQ(sizeof(kHello), num_bytes);
    434     EXPECT_STREQ(kHello, buffer);
    435 
    436     // 2. Write a message to |client_mp| (attaching nothing).
    437     const char kWorld[] = "world!";
    438     EXPECT_EQ(MOJO_RESULT_OK,
    439               MojoWriteMessage(client_mp, kWorld,
    440                                static_cast<uint32_t>(sizeof(kWorld)), NULL, 0,
    441                                MOJO_WRITE_MESSAGE_FLAG_NONE));
    442 
    443     // 4. Read a message from |client_mp|, which should have |mp1| attached.
    444     EXPECT_EQ(MOJO_RESULT_OK,
    445               MojoWait(client_mp, MOJO_HANDLE_SIGNAL_READABLE,
    446                        MOJO_DEADLINE_INDEFINITE));
    447     // TODO(vtl): If the scope were to end here (and |client_mp| closed), we'd
    448     // die (again due to |Channel::HandleLocalError()|).
    449     memset(buffer, 0, sizeof(buffer));
    450     num_bytes = static_cast<uint32_t>(sizeof(buffer));
    451     MojoHandle mp1 = MOJO_HANDLE_INVALID;
    452     uint32_t num_handles = 1;
    453     EXPECT_EQ(MOJO_RESULT_OK,
    454               MojoReadMessage(client_mp, buffer, &num_bytes, &mp1, &num_handles,
    455                               MOJO_READ_MESSAGE_FLAG_NONE));
    456     const char kBar[] = "Bar";
    457     EXPECT_EQ(sizeof(kBar), num_bytes);
    458     EXPECT_STREQ(kBar, buffer);
    459     EXPECT_EQ(1u, num_handles);
    460     EXPECT_NE(mp1, MOJO_HANDLE_INVALID);
    461     // TODO(vtl): If the scope were to end here (and the two handles closed),
    462     // we'd die due to |Channel::RunRemoteMessagePipeEndpoint()| not handling
    463     // write errors (assuming the parent had closed the pipe).
    464 
    465     // 6. Close |client_mp|.
    466     EXPECT_EQ(MOJO_RESULT_OK, MojoClose(client_mp));
    467 
    468     // Create a new message pipe (endpoints |mp2| and |mp3|).
    469     MojoHandle mp2, mp3;
    470     EXPECT_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(NULL, &mp2, &mp3));
    471 
    472     // 7. Write a message to |mp3|.
    473     const char kBaz[] = "baz";
    474     EXPECT_EQ(MOJO_RESULT_OK,
    475               MojoWriteMessage(mp3, kBaz,
    476                                static_cast<uint32_t>(sizeof(kBaz)), NULL, 0,
    477                                MOJO_WRITE_MESSAGE_FLAG_NONE));
    478 
    479     // 8. Close |mp3|.
    480     EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mp3));
    481 
    482     // 9. Write a message to |mp1|, attaching |mp2|.
    483     const char kQuux[] = "quux";
    484     EXPECT_EQ(MOJO_RESULT_OK,
    485               MojoWriteMessage(mp1, kQuux,
    486                                static_cast<uint32_t>(sizeof(kQuux)), &mp2, 1,
    487                                MOJO_WRITE_MESSAGE_FLAG_NONE));
    488     mp2 = MOJO_HANDLE_INVALID;
    489 
    490     // 3. Read a message from |mp1|.
    491     EXPECT_EQ(MOJO_RESULT_OK,
    492               MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE,
    493                        MOJO_DEADLINE_INDEFINITE));
    494     memset(buffer, 0, sizeof(buffer));
    495     num_bytes = static_cast<uint32_t>(sizeof(buffer));
    496     EXPECT_EQ(MOJO_RESULT_OK,
    497               MojoReadMessage(mp1, buffer, &num_bytes, NULL, NULL,
    498                               MOJO_READ_MESSAGE_FLAG_NONE));
    499     const char kFoo[] = "FOO";
    500     EXPECT_EQ(sizeof(kFoo), num_bytes);
    501     EXPECT_STREQ(kFoo, buffer);
    502 
    503     // 11. Wait on |mp1| (which should eventually fail) and then close it.
    504     EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
    505               MojoWait(mp1, MOJO_HANDLE_SIGNAL_READABLE,
    506                        MOJO_DEADLINE_INDEFINITE));
    507     EXPECT_EQ(MOJO_RESULT_OK, MojoClose(mp1));
    508   }
    509 
    510   EXPECT_TRUE(test::Shutdown());
    511 }
    512 
    513 // TODO(vtl): Test immediate write & close.
    514 // TODO(vtl): Test broken-connection cases.
    515 
    516 }  // namespace
    517 }  // namespace embedder
    518 }  // namespace mojo
    519