text: // Copyright 2013 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include <stdint.h>
  #include <stdio.h>
  #include <string.h>
  
  #include <string>
  #include <vector>
  
  #include "base/bind.h"
  #include "base/files/file_path.h"
  #include "base/files/file_util.h"
  #include "base/files/scoped_file.h"
  #include "base/files/scoped_temp_dir.h"
  #include "base/location.h"
  #include "base/logging.h"
  #include "build/build_config.h"  // TODO(vtl): Remove this.
  #include "testing/gtest/include/gtest/gtest.h"
  #include "third_party/mojo/src/mojo/edk/embedder/platform_shared_buffer.h"
  #include "third_party/mojo/src/mojo/edk/embedder/scoped_platform_handle.h"
  #include "third_party/mojo/src/mojo/edk/system/channel.h"
  #include "third_party/mojo/src/mojo/edk/system/dispatcher.h"
  #include "third_party/mojo/src/mojo/edk/system/message_pipe.h"
  #include "third_party/mojo/src/mojo/edk/system/message_pipe_test_utils.h"
  #include "third_party/mojo/src/mojo/edk/system/platform_handle_dispatcher.h"
  #include "third_party/mojo/src/mojo/edk/system/raw_channel.h"
  #include "third_party/mojo/src/mojo/edk/system/shared_buffer_dispatcher.h"
  #include "third_party/mojo/src/mojo/edk/system/test_utils.h"
  #include "third_party/mojo/src/mojo/edk/test/test_utils.h"
  
  namespace mojo {
  namespace system {
  namespace {
  
  class MultiprocessMessagePipeTest
      : public test::MultiprocessMessagePipeTestBase {};
  
  // For each message received, sends a reply message with the same contents
  // repeated twice, until the other end is closed or it receives "quitquitquit"
  // (which it doesn't reply to). It'll return the number of messages received,
  // not including any "quitquitquit" message, modulo 100.
  MOJO_MULTIPROCESS_TEST_CHILD_MAIN(EchoEcho) {
    embedder::SimplePlatformSupport platform_support;
    test::ChannelThread channel_thread(&platform_support);
    embedder::ScopedPlatformHandle client_platform_handle =
        mojo::test::MultiprocessTestHelper::client_platform_handle.Pass();
    CHECK(client_platform_handle.is_valid());
    scoped_refptr<ChannelEndpoint> ep;
    scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
    channel_thread.Start(client_platform_handle.Pass(), ep);
  
    const std::string quitquitquit("quitquitquit");
    int rv = 0;
    for (;; rv = (rv + 1) % 100) {
      // Wait for our end of the message pipe to be readable.
      HandleSignalsState hss;
      MojoResult result =
          test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss);
      if (result != MOJO_RESULT_OK) {
        // It was closed, probably.
        CHECK_EQ(result, MOJO_RESULT_FAILED_PRECONDITION);
        CHECK_EQ(hss.satisfied_signals, MOJO_HANDLE_SIGNAL_PEER_CLOSED);
        CHECK_EQ(hss.satisfiable_signals, MOJO_HANDLE_SIGNAL_PEER_CLOSED);
        break;
      } else {
        CHECK((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
        CHECK((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
      }
  
      std::string read_buffer(1000, '\0');
      uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size());
      CHECK_EQ(mp->ReadMessage(0, UserPointer<void>(&read_buffer[0]),
                               MakeUserPointer(&read_buffer_size), nullptr,
                               nullptr, MOJO_READ_MESSAGE_FLAG_NONE),
               MOJO_RESULT_OK);
      read_buffer.resize(read_buffer_size);
      VLOG(2) << "Child got: " << read_buffer;
  
      if (read_buffer == quitquitquit) {
        VLOG(2) << "Child quitting.";
        break;
      }
  
      std::string write_buffer = read_buffer + read_buffer;
      CHECK_EQ(mp->WriteMessage(0, UserPointer<const void>(write_buffer.data()),
                                static_cast<uint32_t>(write_buffer.size()),
                                nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE),
               MOJO_RESULT_OK);
    }
  
    mp->Close(0);
    return rv;
  }
  
  // Sends "hello" to child, and expects "hellohello" back.
  #if defined(OS_ANDROID)
  // Android multi-process tests are not executing the new process. This is flaky.
  #define MAYBE_Basic DISABLED_Basic
  #else
  #define MAYBE_Basic Basic
  #endif  // defined(OS_ANDROID)
  TEST_F(MultiprocessMessagePipeTest, MAYBE_Basic) {
    helper()->StartChild("EchoEcho");
  
    scoped_refptr<ChannelEndpoint> ep;
    scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
    Init(ep);
  
    std::string hello("hello");
    EXPECT_EQ(MOJO_RESULT_OK,
              mp->WriteMessage(0, UserPointer<const void>(hello.data()),
                               static_cast<uint32_t>(hello.size()), nullptr,
                               MOJO_WRITE_MESSAGE_FLAG_NONE));
  
    HandleSignalsState hss;
    EXPECT_EQ(MOJO_RESULT_OK,
              test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
    // The child may or may not have closed its end of the message pipe and died
    // (and we may or may not know it yet), so our end may or may not appear as
    // writable.
    EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
    EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
  
    std::string read_buffer(1000, '\0');
    uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size());
    CHECK_EQ(mp->ReadMessage(0, UserPointer<void>(&read_buffer[0]),
                             MakeUserPointer(&read_buffer_size), nullptr, nullptr,
                             MOJO_READ_MESSAGE_FLAG_NONE),
             MOJO_RESULT_OK);
    read_buffer.resize(read_buffer_size);
    VLOG(2) << "Parent got: " << read_buffer;
    EXPECT_EQ(hello + hello, read_buffer);
  
    mp->Close(0);
  
    // We sent one message.
    EXPECT_EQ(1 % 100, helper()->WaitForChildShutdown());
  }
  
  // Sends a bunch of messages to the child. Expects them "repeated" back. Waits
  // for the child to close its end before quitting.
  #if defined(OS_ANDROID)
  // Android multi-process tests are not executing the new process. This is flaky.
  #define MAYBE_QueueMessages DISABLED_QueueMessages
  #else
  #define MAYBE_QueueMessages QueueMessages
  #endif  // defined(OS_ANDROID)
  TEST_F(MultiprocessMessagePipeTest, DISABLED_QueueMessages) {
    helper()->StartChild("EchoEcho");
  
    scoped_refptr<ChannelEndpoint> ep;
    scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
    Init(ep);
  
    static const size_t kNumMessages = 1001;
    for (size_t i = 0; i < kNumMessages; i++) {
      std::string write_buffer(i, 'A' + (i % 26));
      EXPECT_EQ(MOJO_RESULT_OK,
                mp->WriteMessage(0, UserPointer<const void>(write_buffer.data()),
                                 static_cast<uint32_t>(write_buffer.size()),
                                 nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE));
    }
  
    const std::string quitquitquit("quitquitquit");
    EXPECT_EQ(MOJO_RESULT_OK,
              mp->WriteMessage(0, UserPointer<const void>(quitquitquit.data()),
                               static_cast<uint32_t>(quitquitquit.size()),
                               nullptr, MOJO_WRITE_MESSAGE_FLAG_NONE));
  
    for (size_t i = 0; i < kNumMessages; i++) {
      HandleSignalsState hss;
      EXPECT_EQ(MOJO_RESULT_OK,
                test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
      // The child may or may not have closed its end of the message pipe and died
      // (and we may or may not know it yet), so our end may or may not appear as
      // writable.
      EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
      EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
  
      std::string read_buffer(kNumMessages * 2, '\0');
      uint32_t read_buffer_size = static_cast<uint32_t>(read_buffer.size());
      CHECK_EQ(mp->ReadMessage(0, UserPointer<void>(&read_buffer[0]),
                               MakeUserPointer(&read_buffer_size), nullptr,
                               nullptr, MOJO_READ_MESSAGE_FLAG_NONE),
               MOJO_RESULT_OK);
      read_buffer.resize(read_buffer_size);
  
      EXPECT_EQ(std::string(i * 2, 'A' + (i % 26)), read_buffer);
    }
  
    // Wait for it to become readable, which should fail (since we sent
    // "quitquitquit").
    HandleSignalsState hss;
    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
              test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
    EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
    EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
  
    mp->Close(0);
  
    EXPECT_EQ(static_cast<int>(kNumMessages % 100),
              helper()->WaitForChildShutdown());
  }
  
  MOJO_MULTIPROCESS_TEST_CHILD_MAIN(CheckSharedBuffer) {
    embedder::SimplePlatformSupport platform_support;
    test::ChannelThread channel_thread(&platform_support);
    embedder::ScopedPlatformHandle client_platform_handle =
        mojo::test::MultiprocessTestHelper::client_platform_handle.Pass();
    CHECK(client_platform_handle.is_valid());
    scoped_refptr<ChannelEndpoint> ep;
    scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
    channel_thread.Start(client_platform_handle.Pass(), ep);
  
    // Wait for the first message from our parent.
    HandleSignalsState hss;
    CHECK_EQ(test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss),
             MOJO_RESULT_OK);
    // In this test, the parent definitely doesn't close its end of the message
    // pipe before we do.
    CHECK_EQ(hss.satisfied_signals,
             MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
    CHECK_EQ(hss.satisfiable_signals, MOJO_HANDLE_SIGNAL_READABLE |
                                          MOJO_HANDLE_SIGNAL_WRITABLE |
                                          MOJO_HANDLE_SIGNAL_PEER_CLOSED);
  
    // It should have a shared buffer.
    std::string read_buffer(100, '\0');
    uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size());
    DispatcherVector dispatchers;
    uint32_t num_dispatchers = 10;  // Maximum number to receive.
    CHECK_EQ(mp->ReadMessage(0, UserPointer<void>(&read_buffer[0]),
                             MakeUserPointer(&num_bytes), &dispatchers,
                             &num_dispatchers, MOJO_READ_MESSAGE_FLAG_NONE),
             MOJO_RESULT_OK);
    read_buffer.resize(num_bytes);
    CHECK_EQ(read_buffer, std::string("go 1"));
    CHECK_EQ(num_dispatchers, 1u);
  
    CHECK_EQ(dispatchers[0]->GetType(), Dispatcher::Type::SHARED_BUFFER);
  
    scoped_refptr<SharedBufferDispatcher> dispatcher(
        static_cast<SharedBufferDispatcher*>(dispatchers[0].get()));
  
    // Make a mapping.
    scoped_ptr<embedder::PlatformSharedBufferMapping> mapping;
    CHECK_EQ(dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping),
             MOJO_RESULT_OK);
    CHECK(mapping);
    CHECK(mapping->GetBase());
    CHECK_EQ(mapping->GetLength(), 100u);
  
    // Write some stuff to the shared buffer.
    static const char kHello[] = "hello";
    memcpy(mapping->GetBase(), kHello, sizeof(kHello));
  
    // We should be able to close the dispatcher now.
    dispatcher->Close();
  
    // And send a message to signal that we've written stuff.
    const std::string go2("go 2");
    CHECK_EQ(mp->WriteMessage(0, UserPointer<const void>(&go2[0]),
                              static_cast<uint32_t>(go2.size()), nullptr,
                              MOJO_WRITE_MESSAGE_FLAG_NONE),
             MOJO_RESULT_OK);
  
    // Now wait for our parent to send us a message.
    hss = HandleSignalsState();
    CHECK_EQ(test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss),
             MOJO_RESULT_OK);
    CHECK_EQ(hss.satisfied_signals,
             MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
    CHECK_EQ(hss.satisfiable_signals, MOJO_HANDLE_SIGNAL_READABLE |
                                          MOJO_HANDLE_SIGNAL_WRITABLE |
                                          MOJO_HANDLE_SIGNAL_PEER_CLOSED);
  
    read_buffer = std::string(100, '\0');
    num_bytes = static_cast<uint32_t>(read_buffer.size());
    CHECK_EQ(mp->ReadMessage(0, UserPointer<void>(&read_buffer[0]),
                             MakeUserPointer(&num_bytes), nullptr, nullptr,
                             MOJO_READ_MESSAGE_FLAG_NONE),
             MOJO_RESULT_OK);
    read_buffer.resize(num_bytes);
    CHECK_EQ(read_buffer, std::string("go 3"));
  
    // It should have written something to the shared buffer.
    static const char kWorld[] = "world!!!";
    CHECK_EQ(memcmp(mapping->GetBase(), kWorld, sizeof(kWorld)), 0);
  
    // And we're done.
    mp->Close(0);
  
    return 0;
  }
  
  #if defined(OS_POSIX) && !defined(OS_ANDROID)
  #define MAYBE_SharedBufferPassing SharedBufferPassing
  #else
  // Not yet implemented (on Windows).
  // Android multi-process tests are not executing the new process. This is flaky.
  #define MAYBE_SharedBufferPassing DISABLED_SharedBufferPassing
  #endif
  TEST_F(MultiprocessMessagePipeTest, MAYBE_SharedBufferPassing) {
    helper()->StartChild("CheckSharedBuffer");
  
    scoped_refptr<ChannelEndpoint> ep;
    scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
    Init(ep);
  
    // Make a shared buffer.
    scoped_refptr<SharedBufferDispatcher> dispatcher;
    EXPECT_EQ(MOJO_RESULT_OK, SharedBufferDispatcher::Create(
                                  platform_support(),
                                  SharedBufferDispatcher::kDefaultCreateOptions,
                                  100, &dispatcher));
    ASSERT_TRUE(dispatcher);
  
    // Make a mapping.
    scoped_ptr<embedder::PlatformSharedBufferMapping> mapping;
    EXPECT_EQ(MOJO_RESULT_OK,
              dispatcher->MapBuffer(0, 100, MOJO_MAP_BUFFER_FLAG_NONE, &mapping));
    ASSERT_TRUE(mapping);
    ASSERT_TRUE(mapping->GetBase());
    ASSERT_EQ(100u, mapping->GetLength());
  
    // Send the shared buffer.
    const std::string go1("go 1");
    DispatcherTransport transport(
        test::DispatcherTryStartTransport(dispatcher.get()));
    ASSERT_TRUE(transport.is_valid());
  
    std::vector<DispatcherTransport> transports;
    transports.push_back(transport);
    EXPECT_EQ(MOJO_RESULT_OK,
              mp->WriteMessage(0, UserPointer<const void>(&go1[0]),
                               static_cast<uint32_t>(go1.size()), &transports,
                               MOJO_WRITE_MESSAGE_FLAG_NONE));
    transport.End();
  
    EXPECT_TRUE(dispatcher->HasOneRef());
    dispatcher = nullptr;
  
    // Wait for a message from the child.
    HandleSignalsState hss;
    EXPECT_EQ(MOJO_RESULT_OK,
              test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
    EXPECT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
    EXPECT_TRUE((hss.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE));
  
    std::string read_buffer(100, '\0');
    uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size());
    EXPECT_EQ(MOJO_RESULT_OK,
              mp->ReadMessage(0, UserPointer<void>(&read_buffer[0]),
                              MakeUserPointer(&num_bytes), nullptr, nullptr,
                              MOJO_READ_MESSAGE_FLAG_NONE));
    read_buffer.resize(num_bytes);
    EXPECT_EQ(std::string("go 2"), read_buffer);
  
    // After we get it, the child should have written something to the shared
    // buffer.
    static const char kHello[] = "hello";
    EXPECT_EQ(0, memcmp(mapping->GetBase(), kHello, sizeof(kHello)));
  
    // Now we'll write some stuff to the shared buffer.
    static const char kWorld[] = "world!!!";
    memcpy(mapping->GetBase(), kWorld, sizeof(kWorld));
  
    // And send a message to signal that we've written stuff.
    const std::string go3("go 3");
    EXPECT_EQ(MOJO_RESULT_OK,
              mp->WriteMessage(0, UserPointer<const void>(&go3[0]),
                               static_cast<uint32_t>(go3.size()), nullptr,
                               MOJO_WRITE_MESSAGE_FLAG_NONE));
  
    // Wait for |mp| to become readable, which should fail.
    hss = HandleSignalsState();
    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
              test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
    EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
    EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
  
    mp->Close(0);
  
    EXPECT_EQ(0, helper()->WaitForChildShutdown());
  }
  
  MOJO_MULTIPROCESS_TEST_CHILD_MAIN(CheckPlatformHandleFile) {
    embedder::SimplePlatformSupport platform_support;
    test::ChannelThread channel_thread(&platform_support);
    embedder::ScopedPlatformHandle client_platform_handle =
        mojo::test::MultiprocessTestHelper::client_platform_handle.Pass();
    CHECK(client_platform_handle.is_valid());
    scoped_refptr<ChannelEndpoint> ep;
    scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
    channel_thread.Start(client_platform_handle.Pass(), ep);
  
    HandleSignalsState hss;
    CHECK_EQ(test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss),
             MOJO_RESULT_OK);
    CHECK_EQ(hss.satisfied_signals,
             MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE);
    CHECK_EQ(hss.satisfiable_signals, MOJO_HANDLE_SIGNAL_READABLE |
                                          MOJO_HANDLE_SIGNAL_WRITABLE |
                                          MOJO_HANDLE_SIGNAL_PEER_CLOSED);
  
    std::string read_buffer(100, '\0');
    uint32_t num_bytes = static_cast<uint32_t>(read_buffer.size());
    DispatcherVector dispatchers;
    uint32_t num_dispatchers = 255;  // Maximum number to receive.
    CHECK_EQ(mp->ReadMessage(0, UserPointer<void>(&read_buffer[0]),
                             MakeUserPointer(&num_bytes), &dispatchers,
                             &num_dispatchers, MOJO_READ_MESSAGE_FLAG_NONE),
             MOJO_RESULT_OK);
    mp->Close(0);
  
    read_buffer.resize(num_bytes);
    char hello[32];
    int num_handles = 0;
    sscanf(read_buffer.c_str(), "%s %d", hello, &num_handles);
    CHECK_EQ(std::string("hello"), std::string(hello));
    CHECK_GT(num_handles, 0);
  
    for (int i = 0; i < num_handles; ++i) {
      CHECK_EQ(dispatchers[i]->GetType(), Dispatcher::Type::PLATFORM_HANDLE);
  
      scoped_refptr<PlatformHandleDispatcher> dispatcher(
          static_cast<PlatformHandleDispatcher*>(dispatchers[i].get()));
      embedder::ScopedPlatformHandle h = dispatcher->PassPlatformHandle().Pass();
      CHECK(h.is_valid());
      dispatcher->Close();
  
      base::ScopedFILE fp(mojo::test::FILEFromPlatformHandle(h.Pass(), "r"));
      CHECK(fp);
      std::string fread_buffer(100, '\0');
      size_t bytes_read =
          fread(&fread_buffer[0], 1, fread_buffer.size(), fp.get());
      fread_buffer.resize(bytes_read);
      CHECK_EQ(fread_buffer, "world");
    }
  
    return 0;
  }
  
  class MultiprocessMessagePipeTestWithPipeCount
      : public test::MultiprocessMessagePipeTestBase,
        public testing::WithParamInterface<size_t> {};
  
  TEST_P(MultiprocessMessagePipeTestWithPipeCount, PlatformHandlePassing) {
    base::ScopedTempDir temp_dir;
    ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
  
    helper()->StartChild("CheckPlatformHandleFile");
  
    scoped_refptr<ChannelEndpoint> ep;
    scoped_refptr<MessagePipe> mp(MessagePipe::CreateLocalProxy(&ep));
    Init(ep);
  
    std::vector<scoped_refptr<PlatformHandleDispatcher>> dispatchers;
    std::vector<DispatcherTransport> transports;
  
    size_t pipe_count = GetParam();
    for (size_t i = 0; i < pipe_count; ++i) {
      base::FilePath unused;
      base::ScopedFILE fp(
          CreateAndOpenTemporaryFileInDir(temp_dir.path(), &unused));
      const std::string world("world");
      CHECK_EQ(fwrite(&world[0], 1, world.size(), fp.get()), world.size());
      fflush(fp.get());
      rewind(fp.get());
  
      scoped_refptr<PlatformHandleDispatcher> dispatcher =
          PlatformHandleDispatcher::Create(embedder::ScopedPlatformHandle(
              mojo::test::PlatformHandleFromFILE(fp.Pass())));
      dispatchers.push_back(dispatcher);
      DispatcherTransport transport(
          test::DispatcherTryStartTransport(dispatcher.get()));
      ASSERT_TRUE(transport.is_valid());
      transports.push_back(transport);
    }
  
    char message[128];
    sprintf(message, "hello %d", static_cast<int>(pipe_count));
    EXPECT_EQ(MOJO_RESULT_OK,
              mp->WriteMessage(0, UserPointer<const void>(message),
                               static_cast<uint32_t>(strlen(message)),
                               &transports, MOJO_WRITE_MESSAGE_FLAG_NONE));
  
    for (size_t i = 0; i < pipe_count; ++i) {
      transports[i].End();
      EXPECT_TRUE(dispatchers[i]->HasOneRef());
    }
  
    dispatchers.clear();
  
    // Wait for it to become readable, which should fail.
    HandleSignalsState hss;
    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
              test::WaitIfNecessary(mp, MOJO_HANDLE_SIGNAL_READABLE, &hss));
    EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
    EXPECT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
  
    mp->Close(0);
  
    EXPECT_EQ(0, helper()->WaitForChildShutdown());
  }
  
  // Not yet implemented (on Windows).
  // Android multi-process tests are not executing the new process. This is flaky.
  #if defined(OS_POSIX) && !defined(OS_ANDROID)
  INSTANTIATE_TEST_CASE_P(PipeCount,
                          MultiprocessMessagePipeTestWithPipeCount,
                          testing::Values(1u, 128u, 140u));
  #endif
  
  }  // namespace
  }  // namespace system
  }  // namespace mojo
  