• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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).
ScopedTestChannel(scoped_refptr<base::TaskRunner> io_thread_task_runner,ScopedPlatformHandle platform_handle)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.)
~ScopedTestChannel()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.
WaitForChannelCreationCompletion()57   void WaitForChannelCreationCompletion() {
58     did_create_channel_event_.Wait();
59   }
60 
bootstrap_message_pipe() const61   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.
channel_info() const65   const ChannelInfo* channel_info() const { return channel_info_; }
66 
67  private:
DidCreateChannel(ChannelInfo * channel_info)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 
DestroyChannel()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:
EmbedderTest()101   EmbedderTest() : test_io_thread_(system::test::TestIOThread::kAutoStart) {}
~EmbedderTest()102   virtual ~EmbedderTest() {}
103 
104  protected:
test_io_thread()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 
TEST_F(EmbedderTest,ChannelsBasic)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 
TEST_F(EmbedderTest,ChannelsHandlePassing)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.)
TEST_F(EmbedderTest,MultiprocessChannels)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 
MOJO_MULTIPROCESS_TEST_CHILD_TEST(MultiprocessChannelsClient)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