aboutsummaryrefslogtreecommitdiff
path: root/mojo/edk/system/multiprocess_message_pipe_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'mojo/edk/system/multiprocess_message_pipe_unittest.cc')
-rw-r--r--mojo/edk/system/multiprocess_message_pipe_unittest.cc1366
1 files changed, 0 insertions, 1366 deletions
diff --git a/mojo/edk/system/multiprocess_message_pipe_unittest.cc b/mojo/edk/system/multiprocess_message_pipe_unittest.cc
deleted file mode 100644
index 37248d1..0000000
--- a/mojo/edk/system/multiprocess_message_pipe_unittest.cc
+++ /dev/null
@@ -1,1366 +0,0 @@
-// 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 <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "base/bind.h"
-#include "base/containers/hash_tables.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/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/run_loop.h"
-#include "base/strings/string_split.h"
-#include "build/build_config.h"
-#include "mojo/edk/embedder/platform_channel_pair.h"
-#include "mojo/edk/embedder/scoped_platform_handle.h"
-#include "mojo/edk/system/handle_signals_state.h"
-#include "mojo/edk/system/test_utils.h"
-#include "mojo/edk/test/mojo_test_base.h"
-#include "mojo/edk/test/test_utils.h"
-#include "mojo/public/c/system/buffer.h"
-#include "mojo/public/c/system/functions.h"
-#include "mojo/public/c/system/types.h"
-#include "mojo/public/cpp/system/simple_watcher.h"
-#include "mojo/public/cpp/system/wait.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-
-namespace mojo {
-namespace edk {
-namespace {
-
-class MultiprocessMessagePipeTest : public test::MojoTestBase {
- protected:
- // Convenience class for tests which will control command-driven children.
- // See the CommandDrivenClient definition below.
- class CommandDrivenClientController {
- public:
- explicit CommandDrivenClientController(MojoHandle h) : h_(h) {}
-
- void Send(const std::string& command) {
- WriteMessage(h_, command);
- EXPECT_EQ("ok", ReadMessage(h_));
- }
-
- void SendHandle(const std::string& name, MojoHandle p) {
- WriteMessageWithHandles(h_, "take:" + name, &p, 1);
- EXPECT_EQ("ok", ReadMessage(h_));
- }
-
- MojoHandle RetrieveHandle(const std::string& name) {
- WriteMessage(h_, "return:" + name);
- MojoHandle p;
- EXPECT_EQ("ok", ReadMessageWithHandles(h_, &p, 1));
- return p;
- }
-
- void Exit() { WriteMessage(h_, "exit"); }
-
- private:
- MojoHandle h_;
- };
-};
-
-class MultiprocessMessagePipeTestWithPeerSupport
- : public MultiprocessMessagePipeTest,
- public testing::WithParamInterface<test::MojoTestBase::LaunchType> {
- protected:
- void SetUp() override {
- test::MojoTestBase::SetUp();
- set_launch_type(GetParam());
- }
-};
-
-// 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.
-DEFINE_TEST_CLIENT_WITH_PIPE(EchoEcho, MultiprocessMessagePipeTest, h) {
- 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 = WaitForSignals(h, 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(MojoReadMessage(h, &read_buffer[0],
- &read_buffer_size, nullptr,
- 0, 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(MojoWriteMessage(h, write_buffer.data(),
- static_cast<uint32_t>(write_buffer.size()),
- nullptr, 0u, MOJO_WRITE_MESSAGE_FLAG_NONE),
- MOJO_RESULT_OK);
- }
-
- return rv;
-}
-
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, Basic) {
- RUN_CHILD_ON_PIPE(EchoEcho, h)
- std::string hello("hello");
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoWriteMessage(h, hello.data(),
- static_cast<uint32_t>(hello.size()), nullptr, 0u,
- MOJO_WRITE_MESSAGE_FLAG_NONE));
-
- HandleSignalsState hss;
- ASSERT_EQ(MOJO_RESULT_OK,
- WaitForSignals(h, 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(MojoReadMessage(h, &read_buffer[0],
- &read_buffer_size, nullptr, 0,
- MOJO_READ_MESSAGE_FLAG_NONE),
- MOJO_RESULT_OK);
- read_buffer.resize(read_buffer_size);
- VLOG(2) << "Parent got: " << read_buffer;
- ASSERT_EQ(hello + hello, read_buffer);
-
- std::string quitquitquit("quitquitquit");
- CHECK_EQ(MojoWriteMessage(h, quitquitquit.data(),
- static_cast<uint32_t>(quitquitquit.size()),
- nullptr, 0u, MOJO_WRITE_MESSAGE_FLAG_NONE),
- MOJO_RESULT_OK);
- END_CHILD_AND_EXPECT_EXIT_CODE(1 % 100);
-}
-
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, QueueMessages) {
- static const size_t kNumMessages = 1001;
- RUN_CHILD_ON_PIPE(EchoEcho, h)
- for (size_t i = 0; i < kNumMessages; i++) {
- std::string write_buffer(i, 'A' + (i % 26));
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoWriteMessage(h, write_buffer.data(),
- static_cast<uint32_t>(write_buffer.size()),
- nullptr, 0u, MOJO_WRITE_MESSAGE_FLAG_NONE));
- }
-
- for (size_t i = 0; i < kNumMessages; i++) {
- HandleSignalsState hss;
- ASSERT_EQ(MOJO_RESULT_OK,
- WaitForSignals(h, 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.
- ASSERT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE));
- ASSERT_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());
- ASSERT_EQ(MojoReadMessage(h, &read_buffer[0],
- &read_buffer_size, nullptr, 0,
- MOJO_READ_MESSAGE_FLAG_NONE),
- MOJO_RESULT_OK);
- read_buffer.resize(read_buffer_size);
-
- ASSERT_EQ(std::string(i * 2, 'A' + (i % 26)), read_buffer);
- }
-
- const std::string quitquitquit("quitquitquit");
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoWriteMessage(h, quitquitquit.data(),
- static_cast<uint32_t>(quitquitquit.size()),
- nullptr, 0u, MOJO_WRITE_MESSAGE_FLAG_NONE));
-
- // Wait for it to become readable, which should fail (since we sent
- // "quitquitquit").
- HandleSignalsState hss;
- ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
- WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss));
- ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
- ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
- END_CHILD_AND_EXPECT_EXIT_CODE(static_cast<int>(kNumMessages % 100));
-}
-
-DEFINE_TEST_CLIENT_WITH_PIPE(CheckSharedBuffer, MultiprocessMessagePipeTest,
- h) {
- // Wait for the first message from our parent.
- HandleSignalsState hss;
- CHECK_EQ(WaitForSignals(h, 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());
- MojoHandle handles[10];
- uint32_t num_handlers = arraysize(handles); // Maximum number to receive
- CHECK_EQ(MojoReadMessage(h, &read_buffer[0],
- &num_bytes, &handles[0],
- &num_handlers, MOJO_READ_MESSAGE_FLAG_NONE),
- MOJO_RESULT_OK);
- read_buffer.resize(num_bytes);
- CHECK_EQ(read_buffer, std::string("go 1"));
- CHECK_EQ(num_handlers, 1u);
-
- // Make a mapping.
- void* buffer;
- CHECK_EQ(MojoMapBuffer(handles[0], 0, 100, &buffer,
- MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE),
- MOJO_RESULT_OK);
-
- // Write some stuff to the shared buffer.
- static const char kHello[] = "hello";
- memcpy(buffer, kHello, sizeof(kHello));
-
- // We should be able to close the dispatcher now.
- MojoClose(handles[0]);
-
- // And send a message to signal that we've written stuff.
- const std::string go2("go 2");
- CHECK_EQ(MojoWriteMessage(h, go2.data(),
- static_cast<uint32_t>(go2.size()), nullptr, 0u,
- MOJO_WRITE_MESSAGE_FLAG_NONE),
- MOJO_RESULT_OK);
-
- // Now wait for our parent to send us a message.
- hss = HandleSignalsState();
- CHECK_EQ(WaitForSignals(h, 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(MojoReadMessage(h, &read_buffer[0], &num_bytes,
- nullptr, 0, 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(buffer, kWorld, sizeof(kWorld)), 0);
-
- // And we're done.
-
- return 0;
-}
-
-TEST_F(MultiprocessMessagePipeTest, SharedBufferPassing) {
- RUN_CHILD_ON_PIPE(CheckSharedBuffer, h)
- // Make a shared buffer.
- MojoCreateSharedBufferOptions options;
- options.struct_size = sizeof(options);
- options.flags = MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE;
-
- MojoHandle shared_buffer;
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoCreateSharedBuffer(&options, 100, &shared_buffer));
-
- // Send the shared buffer.
- const std::string go1("go 1");
-
- MojoHandle duplicated_shared_buffer;
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoDuplicateBufferHandle(
- shared_buffer,
- nullptr,
- &duplicated_shared_buffer));
- MojoHandle handles[1];
- handles[0] = duplicated_shared_buffer;
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoWriteMessage(h, &go1[0],
- static_cast<uint32_t>(go1.size()), &handles[0],
- arraysize(handles),
- MOJO_WRITE_MESSAGE_FLAG_NONE));
-
- // Wait for a message from the child.
- HandleSignalsState hss;
- ASSERT_EQ(MOJO_RESULT_OK,
- WaitForSignals(h, 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());
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoReadMessage(h, &read_buffer[0],
- &num_bytes, nullptr, 0,
- MOJO_READ_MESSAGE_FLAG_NONE));
- read_buffer.resize(num_bytes);
- ASSERT_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";
- void* buffer;
- CHECK_EQ(MojoMapBuffer(shared_buffer, 0, 100, &buffer,
- MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE),
- MOJO_RESULT_OK);
- ASSERT_EQ(0, memcmp(buffer, kHello, sizeof(kHello)));
-
- // Now we'll write some stuff to the shared buffer.
- static const char kWorld[] = "world!!!";
- memcpy(buffer, kWorld, sizeof(kWorld));
-
- // And send a message to signal that we've written stuff.
- const std::string go3("go 3");
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoWriteMessage(h, &go3[0],
- static_cast<uint32_t>(go3.size()), nullptr, 0u,
- MOJO_WRITE_MESSAGE_FLAG_NONE));
-
- // Wait for |h| to become readable, which should fail.
- hss = HandleSignalsState();
- ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
- WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss));
- ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
- ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
- END_CHILD()
-}
-
-DEFINE_TEST_CLIENT_WITH_PIPE(CheckPlatformHandleFile,
- MultiprocessMessagePipeTest, h) {
- HandleSignalsState hss;
- CHECK_EQ(WaitForSignals(h, 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());
- MojoHandle handles[255]; // Maximum number to receive.
- uint32_t num_handlers = arraysize(handles);
-
- CHECK_EQ(MojoReadMessage(h, &read_buffer[0],
- &num_bytes, &handles[0],
- &num_handlers, MOJO_READ_MESSAGE_FLAG_NONE),
- MOJO_RESULT_OK);
-
- 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) {
- ScopedPlatformHandle h;
- CHECK_EQ(PassWrappedPlatformHandle(handles[i], &h), MOJO_RESULT_OK);
- CHECK(h.is_valid());
- MojoClose(handles[i]);
-
- base::ScopedFILE fp(test::FILEFromPlatformHandle(std::move(h), "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 MultiprocessMessagePipeTest,
- public testing::WithParamInterface<size_t> {};
-
-TEST_P(MultiprocessMessagePipeTestWithPipeCount, PlatformHandlePassing) {
- base::ScopedTempDir temp_dir;
- ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
-
- RUN_CHILD_ON_PIPE(CheckPlatformHandleFile, h)
- std::vector<MojoHandle> handles;
-
- size_t pipe_count = GetParam();
- for (size_t i = 0; i < pipe_count; ++i) {
- base::FilePath unused;
- base::ScopedFILE fp(
- CreateAndOpenTemporaryFileInDir(temp_dir.GetPath(), &unused));
- const std::string world("world");
- CHECK_EQ(fwrite(&world[0], 1, world.size(), fp.get()), world.size());
- fflush(fp.get());
- rewind(fp.get());
- MojoHandle handle;
- ASSERT_EQ(
- CreatePlatformHandleWrapper(
- ScopedPlatformHandle(test::PlatformHandleFromFILE(std::move(fp))),
- &handle),
- MOJO_RESULT_OK);
- handles.push_back(handle);
- }
-
- char message[128];
- snprintf(message, sizeof(message), "hello %d",
- static_cast<int>(pipe_count));
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoWriteMessage(h, message,
- static_cast<uint32_t>(strlen(message)),
- &handles[0],
- static_cast<uint32_t>(handles.size()),
- MOJO_WRITE_MESSAGE_FLAG_NONE));
-
- // Wait for it to become readable, which should fail.
- HandleSignalsState hss;
- ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
- WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE, &hss));
- ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfied_signals);
- ASSERT_EQ(MOJO_HANDLE_SIGNAL_PEER_CLOSED, hss.satisfiable_signals);
- END_CHILD()
-}
-
-// Android multi-process tests are not executing the new process. This is flaky.
-#if !defined(OS_ANDROID)
-INSTANTIATE_TEST_CASE_P(PipeCount,
- MultiprocessMessagePipeTestWithPipeCount,
- // TODO(rockot): Re-enable the 140-pipe case when
- // ChannelPosix has support for sending lots of handles.
- testing::Values(1u, 128u /*, 140u*/));
-#endif
-
-DEFINE_TEST_CLIENT_WITH_PIPE(CheckMessagePipe, MultiprocessMessagePipeTest, h) {
- // Wait for the first message from our parent.
- HandleSignalsState hss;
- CHECK_EQ(WaitForSignals(h, 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 message pipe.
- MojoHandle handles[10];
- uint32_t num_handlers = arraysize(handles);
- CHECK_EQ(MojoReadMessage(h, nullptr,
- nullptr, &handles[0],
- &num_handlers, MOJO_READ_MESSAGE_FLAG_NONE),
- MOJO_RESULT_OK);
- CHECK_EQ(num_handlers, 1u);
-
- // Read data from the received message pipe.
- CHECK_EQ(WaitForSignals(handles[0], 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 read_buffer_size = static_cast<uint32_t>(read_buffer.size());
- CHECK_EQ(MojoReadMessage(handles[0], &read_buffer[0],
- &read_buffer_size, nullptr,
- 0, MOJO_READ_MESSAGE_FLAG_NONE),
- MOJO_RESULT_OK);
- read_buffer.resize(read_buffer_size);
- CHECK_EQ(read_buffer, std::string("hello"));
-
- // Now write some data into the message pipe.
- std::string write_buffer = "world";
- CHECK_EQ(MojoWriteMessage(handles[0], write_buffer.data(),
- static_cast<uint32_t>(write_buffer.size()), nullptr,
- 0u, MOJO_WRITE_MESSAGE_FLAG_NONE),
- MOJO_RESULT_OK);
- MojoClose(handles[0]);
- return 0;
-}
-
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, MessagePipePassing) {
- RUN_CHILD_ON_PIPE(CheckMessagePipe, h)
- MojoCreateSharedBufferOptions options;
- options.struct_size = sizeof(options);
- options.flags = MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE;
-
- MojoHandle mp1, mp2;
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoCreateMessagePipe(nullptr, &mp1, &mp2));
-
- // Write a string into one end of the new message pipe and send the other
- // end.
- const std::string hello("hello");
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoWriteMessage(mp1, &hello[0],
- static_cast<uint32_t>(hello.size()), nullptr, 0,
- MOJO_WRITE_MESSAGE_FLAG_NONE));
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoWriteMessage(h, nullptr, 0, &mp2, 1,
- MOJO_WRITE_MESSAGE_FLAG_NONE));
-
- // Wait for a message from the child.
- HandleSignalsState hss;
- ASSERT_EQ(MOJO_RESULT_OK,
- WaitForSignals(mp1, 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 read_buffer_size = static_cast<uint32_t>(read_buffer.size());
- CHECK_EQ(MojoReadMessage(mp1, &read_buffer[0],
- &read_buffer_size, nullptr,
- 0, MOJO_READ_MESSAGE_FLAG_NONE),
- MOJO_RESULT_OK);
- read_buffer.resize(read_buffer_size);
- CHECK_EQ(read_buffer, std::string("world"));
-
- MojoClose(mp1);
- END_CHILD()
-}
-
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, MessagePipeTwoPassing) {
- RUN_CHILD_ON_PIPE(CheckMessagePipe, h)
- MojoHandle mp1, mp2;
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoCreateMessagePipe(nullptr, &mp2, &mp1));
-
- // Write a string into one end of the new message pipe and send the other
- // end.
- const std::string hello("hello");
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoWriteMessage(mp1, &hello[0],
- static_cast<uint32_t>(hello.size()), nullptr, 0u,
- MOJO_WRITE_MESSAGE_FLAG_NONE));
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoWriteMessage(h, nullptr, 0u, &mp2, 1u,
- MOJO_WRITE_MESSAGE_FLAG_NONE));
-
- // Wait for a message from the child.
- HandleSignalsState hss;
- ASSERT_EQ(MOJO_RESULT_OK,
- WaitForSignals(mp1, 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 read_buffer_size = static_cast<uint32_t>(read_buffer.size());
- CHECK_EQ(MojoReadMessage(mp1, &read_buffer[0],
- &read_buffer_size, nullptr,
- 0, MOJO_READ_MESSAGE_FLAG_NONE),
- MOJO_RESULT_OK);
- read_buffer.resize(read_buffer_size);
- CHECK_EQ(read_buffer, std::string("world"));
- END_CHILD();
-}
-
-DEFINE_TEST_CLIENT_WITH_PIPE(DataPipeConsumer, MultiprocessMessagePipeTest, h) {
- // Wait for the first message from our parent.
- HandleSignalsState hss;
- CHECK_EQ(WaitForSignals(h, 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 message pipe.
- MojoHandle handles[10];
- uint32_t num_handlers = arraysize(handles);
- CHECK_EQ(MojoReadMessage(h, nullptr,
- nullptr, &handles[0],
- &num_handlers, MOJO_READ_MESSAGE_FLAG_NONE),
- MOJO_RESULT_OK);
- CHECK_EQ(num_handlers, 1u);
-
- // Read data from the received message pipe.
- CHECK_EQ(WaitForSignals(handles[0], 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 read_buffer_size = static_cast<uint32_t>(read_buffer.size());
- CHECK_EQ(MojoReadMessage(handles[0], &read_buffer[0],
- &read_buffer_size, nullptr,
- 0, MOJO_READ_MESSAGE_FLAG_NONE),
- MOJO_RESULT_OK);
- read_buffer.resize(read_buffer_size);
- CHECK_EQ(read_buffer, std::string("hello"));
-
- // Now write some data into the message pipe.
- std::string write_buffer = "world";
- CHECK_EQ(MojoWriteMessage(handles[0], write_buffer.data(),
- static_cast<uint32_t>(write_buffer.size()),
- nullptr, 0u, MOJO_WRITE_MESSAGE_FLAG_NONE),
- MOJO_RESULT_OK);
- MojoClose(handles[0]);
- return 0;
-}
-
-TEST_F(MultiprocessMessagePipeTest, DataPipeConsumer) {
- RUN_CHILD_ON_PIPE(DataPipeConsumer, h)
- MojoCreateSharedBufferOptions options;
- options.struct_size = sizeof(options);
- options.flags = MOJO_CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE;
-
- MojoHandle mp1, mp2;
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoCreateMessagePipe(nullptr, &mp2, &mp1));
-
- // Write a string into one end of the new message pipe and send the other
- // end.
- const std::string hello("hello");
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoWriteMessage(mp1, &hello[0],
- static_cast<uint32_t>(hello.size()), nullptr, 0u,
- MOJO_WRITE_MESSAGE_FLAG_NONE));
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoWriteMessage(h, nullptr, 0, &mp2, 1u,
- MOJO_WRITE_MESSAGE_FLAG_NONE));
-
- // Wait for a message from the child.
- HandleSignalsState hss;
- ASSERT_EQ(MOJO_RESULT_OK,
- WaitForSignals(mp1, 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 read_buffer_size = static_cast<uint32_t>(read_buffer.size());
- CHECK_EQ(MojoReadMessage(mp1, &read_buffer[0],
- &read_buffer_size, nullptr,
- 0, MOJO_READ_MESSAGE_FLAG_NONE),
- MOJO_RESULT_OK);
- read_buffer.resize(read_buffer_size);
- CHECK_EQ(read_buffer, std::string("world"));
-
- MojoClose(mp1);
- END_CHILD();
-}
-
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, CreateMessagePipe) {
- MojoHandle p0, p1;
- CreateMessagePipe(&p0, &p1);
- VerifyTransmission(p0, p1, std::string(10 * 1024 * 1024, 'a'));
- VerifyTransmission(p1, p0, std::string(10 * 1024 * 1024, 'e'));
-
- CloseHandle(p0);
- CloseHandle(p1);
-}
-
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, PassMessagePipeLocal) {
- MojoHandle p0, p1;
- CreateMessagePipe(&p0, &p1);
- VerifyTransmission(p0, p1, "testing testing");
- VerifyTransmission(p1, p0, "one two three");
-
- MojoHandle p2, p3;
-
- CreateMessagePipe(&p2, &p3);
- VerifyTransmission(p2, p3, "testing testing");
- VerifyTransmission(p3, p2, "one two three");
-
- // Pass p2 over p0 to p1.
- const std::string message = "ceci n'est pas une pipe";
- WriteMessageWithHandles(p0, message, &p2, 1);
- EXPECT_EQ(message, ReadMessageWithHandles(p1, &p2, 1));
-
- CloseHandle(p0);
- CloseHandle(p1);
-
- // Verify that the received handle (now in p2) still works.
- VerifyTransmission(p2, p3, "Easy come, easy go; will you let me go?");
- VerifyTransmission(p3, p2, "Bismillah! NO! We will not let you go!");
-
- CloseHandle(p2);
- CloseHandle(p3);
-}
-
-// Echos the primordial channel until "exit".
-DEFINE_TEST_CLIENT_WITH_PIPE(ChannelEchoClient, MultiprocessMessagePipeTest,
- h) {
- for (;;) {
- std::string message = ReadMessage(h);
- if (message == "exit")
- break;
- WriteMessage(h, message);
- }
- return 0;
-}
-
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, MultiprocessChannelPipe) {
- RUN_CHILD_ON_PIPE(ChannelEchoClient, h)
- VerifyEcho(h, "in an interstellar burst");
- VerifyEcho(h, "i am back to save the universe");
- VerifyEcho(h, std::string(10 * 1024 * 1024, 'o'));
-
- WriteMessage(h, "exit");
- END_CHILD()
-}
-
-// Receives a pipe handle from the primordial channel and echos on it until
-// "exit". Used to test simple pipe transfer across processes via channels.
-DEFINE_TEST_CLIENT_WITH_PIPE(EchoServiceClient, MultiprocessMessagePipeTest,
- h) {
- MojoHandle p;
- ReadMessageWithHandles(h, &p, 1);
- for (;;) {
- std::string message = ReadMessage(p);
- if (message == "exit")
- break;
- WriteMessage(p, message);
- }
- return 0;
-}
-
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport,
- PassMessagePipeCrossProcess) {
- MojoHandle p0, p1;
- CreateMessagePipe(&p0, &p1);
- RUN_CHILD_ON_PIPE(EchoServiceClient, h)
-
- // Pass one end of the pipe to the other process.
- WriteMessageWithHandles(h, "here take this", &p1, 1);
-
- VerifyEcho(p0, "and you may ask yourself");
- VerifyEcho(p0, "where does that highway go?");
- VerifyEcho(p0, std::string(20 * 1024 * 1024, 'i'));
-
- WriteMessage(p0, "exit");
- END_CHILD()
- CloseHandle(p0);
-}
-
-// Receives a pipe handle from the primordial channel and reads new handles
-// from it. Each read handle establishes a new echo channel.
-DEFINE_TEST_CLIENT_WITH_PIPE(EchoServiceFactoryClient,
- MultiprocessMessagePipeTest, h) {
- MojoHandle p;
- ReadMessageWithHandles(h, &p, 1);
-
- std::vector<Handle> handles(2);
- handles[0] = Handle(h);
- handles[1] = Handle(p);
- std::vector<MojoHandleSignals> signals(2, MOJO_HANDLE_SIGNAL_READABLE);
- for (;;) {
- size_t index;
- CHECK_EQ(
- mojo::WaitMany(handles.data(), signals.data(), handles.size(), &index),
- MOJO_RESULT_OK);
- DCHECK_LE(index, handles.size());
- if (index == 0) {
- // If data is available on the first pipe, it should be an exit command.
- EXPECT_EQ(std::string("exit"), ReadMessage(h));
- break;
- } else if (index == 1) {
- // If the second pipe, it should be a new handle requesting echo service.
- MojoHandle echo_request;
- ReadMessageWithHandles(p, &echo_request, 1);
- handles.push_back(Handle(echo_request));
- signals.push_back(MOJO_HANDLE_SIGNAL_READABLE);
- } else {
- // Otherwise it was one of our established echo pipes. Echo!
- WriteMessage(handles[index].value(), ReadMessage(handles[index].value()));
- }
- }
-
- for (size_t i = 1; i < handles.size(); ++i)
- CloseHandle(handles[i].value());
-
- return 0;
-}
-
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport,
- PassMoarMessagePipesCrossProcess) {
- MojoHandle echo_factory_proxy, echo_factory_request;
- CreateMessagePipe(&echo_factory_proxy, &echo_factory_request);
-
- MojoHandle echo_proxy_a, echo_request_a;
- CreateMessagePipe(&echo_proxy_a, &echo_request_a);
-
- MojoHandle echo_proxy_b, echo_request_b;
- CreateMessagePipe(&echo_proxy_b, &echo_request_b);
-
- MojoHandle echo_proxy_c, echo_request_c;
- CreateMessagePipe(&echo_proxy_c, &echo_request_c);
-
- RUN_CHILD_ON_PIPE(EchoServiceFactoryClient, h)
- WriteMessageWithHandles(
- h, "gief factory naow plz", &echo_factory_request, 1);
-
- WriteMessageWithHandles(echo_factory_proxy, "give me an echo service plz!",
- &echo_request_a, 1);
- WriteMessageWithHandles(echo_factory_proxy, "give me one too!",
- &echo_request_b, 1);
-
- VerifyEcho(echo_proxy_a, "i came here for an argument");
- VerifyEcho(echo_proxy_a, "shut your festering gob");
- VerifyEcho(echo_proxy_a, "mumble mumble mumble");
-
- VerifyEcho(echo_proxy_b, "wubalubadubdub");
- VerifyEcho(echo_proxy_b, "wubalubadubdub");
-
- WriteMessageWithHandles(echo_factory_proxy, "hook me up also thanks",
- &echo_request_c, 1);
-
- VerifyEcho(echo_proxy_a, "the frobinators taste like frobinators");
- VerifyEcho(echo_proxy_b, "beep bop boop");
- VerifyEcho(echo_proxy_c, "zzzzzzzzzzzzzzzzzzzzzzzzzz");
-
- WriteMessage(h, "exit");
- END_CHILD()
-
- CloseHandle(echo_factory_proxy);
- CloseHandle(echo_proxy_a);
- CloseHandle(echo_proxy_b);
- CloseHandle(echo_proxy_c);
-}
-
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport,
- ChannelPipesWithMultipleChildren) {
- RUN_CHILD_ON_PIPE(ChannelEchoClient, a)
- RUN_CHILD_ON_PIPE(ChannelEchoClient, b)
- VerifyEcho(a, "hello child 0");
- VerifyEcho(b, "hello child 1");
-
- WriteMessage(a, "exit");
- WriteMessage(b, "exit");
- END_CHILD()
- END_CHILD()
-}
-
-// Reads and turns a pipe handle some number of times to create lots of
-// transient proxies.
-DEFINE_TEST_CLIENT_TEST_WITH_PIPE(PingPongPipeClient,
- MultiprocessMessagePipeTest, h) {
- const size_t kNumBounces = 50;
- MojoHandle p0, p1;
- ReadMessageWithHandles(h, &p0, 1);
- ReadMessageWithHandles(h, &p1, 1);
- for (size_t i = 0; i < kNumBounces; ++i) {
- WriteMessageWithHandles(h, "", &p1, 1);
- ReadMessageWithHandles(h, &p1, 1);
- }
- WriteMessageWithHandles(h, "", &p0, 1);
- WriteMessage(p1, "bye");
- MojoClose(p1);
- EXPECT_EQ("quit", ReadMessage(h));
-}
-
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, PingPongPipe) {
- MojoHandle p0, p1;
- CreateMessagePipe(&p0, &p1);
-
- RUN_CHILD_ON_PIPE(PingPongPipeClient, h)
- const size_t kNumBounces = 50;
- WriteMessageWithHandles(h, "", &p0, 1);
- WriteMessageWithHandles(h, "", &p1, 1);
- for (size_t i = 0; i < kNumBounces; ++i) {
- ReadMessageWithHandles(h, &p1, 1);
- WriteMessageWithHandles(h, "", &p1, 1);
- }
- ReadMessageWithHandles(h, &p0, 1);
- WriteMessage(h, "quit");
- END_CHILD()
-
- EXPECT_EQ("bye", ReadMessage(p0));
-
- // We should still be able to observe peer closure from the other end.
- EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(p0, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
-}
-
-// Parses commands from the parent pipe and does whatever it's asked to do.
-DEFINE_TEST_CLIENT_WITH_PIPE(CommandDrivenClient, MultiprocessMessagePipeTest,
- h) {
- base::hash_map<std::string, MojoHandle> named_pipes;
- for (;;) {
- MojoHandle p;
- auto parts = base::SplitString(ReadMessageWithOptionalHandle(h, &p), ":",
- base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
- CHECK(!parts.empty());
- std::string command = parts[0];
- if (command == "take") {
- // Take a pipe.
- CHECK_EQ(parts.size(), 2u);
- CHECK_NE(p, MOJO_HANDLE_INVALID);
- named_pipes[parts[1]] = p;
- WriteMessage(h, "ok");
- } else if (command == "return") {
- // Return a pipe.
- CHECK_EQ(parts.size(), 2u);
- CHECK_EQ(p, MOJO_HANDLE_INVALID);
- p = named_pipes[parts[1]];
- CHECK_NE(p, MOJO_HANDLE_INVALID);
- named_pipes.erase(parts[1]);
- WriteMessageWithHandles(h, "ok", &p, 1);
- } else if (command == "say") {
- // Say something to a named pipe.
- CHECK_EQ(parts.size(), 3u);
- CHECK_EQ(p, MOJO_HANDLE_INVALID);
- p = named_pipes[parts[1]];
- CHECK_NE(p, MOJO_HANDLE_INVALID);
- CHECK(!parts[2].empty());
- WriteMessage(p, parts[2]);
- WriteMessage(h, "ok");
- } else if (command == "hear") {
- // Expect to read something from a named pipe.
- CHECK_EQ(parts.size(), 3u);
- CHECK_EQ(p, MOJO_HANDLE_INVALID);
- p = named_pipes[parts[1]];
- CHECK_NE(p, MOJO_HANDLE_INVALID);
- CHECK(!parts[2].empty());
- CHECK_EQ(parts[2], ReadMessage(p));
- WriteMessage(h, "ok");
- } else if (command == "pass") {
- // Pass one named pipe over another named pipe.
- CHECK_EQ(parts.size(), 3u);
- CHECK_EQ(p, MOJO_HANDLE_INVALID);
- p = named_pipes[parts[1]];
- MojoHandle carrier = named_pipes[parts[2]];
- CHECK_NE(p, MOJO_HANDLE_INVALID);
- CHECK_NE(carrier, MOJO_HANDLE_INVALID);
- named_pipes.erase(parts[1]);
- WriteMessageWithHandles(carrier, "got a pipe for ya", &p, 1);
- WriteMessage(h, "ok");
- } else if (command == "catch") {
- // Expect to receive one named pipe from another named pipe.
- CHECK_EQ(parts.size(), 3u);
- CHECK_EQ(p, MOJO_HANDLE_INVALID);
- MojoHandle carrier = named_pipes[parts[2]];
- CHECK_NE(carrier, MOJO_HANDLE_INVALID);
- ReadMessageWithHandles(carrier, &p, 1);
- CHECK_NE(p, MOJO_HANDLE_INVALID);
- named_pipes[parts[1]] = p;
- WriteMessage(h, "ok");
- } else if (command == "exit") {
- CHECK_EQ(parts.size(), 1u);
- break;
- }
- }
-
- for (auto& pipe : named_pipes)
- CloseHandle(pipe.second);
-
- return 0;
-}
-
-TEST_F(MultiprocessMessagePipeTest, ChildToChildPipes) {
- RUN_CHILD_ON_PIPE(CommandDrivenClient, h0)
- RUN_CHILD_ON_PIPE(CommandDrivenClient, h1)
- CommandDrivenClientController a(h0);
- CommandDrivenClientController b(h1);
-
- // Create a pipe and pass each end to a different client.
- MojoHandle p0, p1;
- CreateMessagePipe(&p0, &p1);
- a.SendHandle("x", p0);
- b.SendHandle("y", p1);
-
- // Make sure they can talk.
- a.Send("say:x:hello");
- b.Send("hear:y:hello");
-
- b.Send("say:y:i love multiprocess pipes!");
- a.Send("hear:x:i love multiprocess pipes!");
-
- a.Exit();
- b.Exit();
- END_CHILD()
- END_CHILD()
-}
-
-TEST_F(MultiprocessMessagePipeTest, MoreChildToChildPipes) {
- RUN_CHILD_ON_PIPE(CommandDrivenClient, h0)
- RUN_CHILD_ON_PIPE(CommandDrivenClient, h1)
- RUN_CHILD_ON_PIPE(CommandDrivenClient, h2)
- RUN_CHILD_ON_PIPE(CommandDrivenClient, h3)
- CommandDrivenClientController a(h0), b(h1), c(h2), d(h3);
-
- // Connect a to b and c to d
-
- MojoHandle p0, p1;
-
- CreateMessagePipe(&p0, &p1);
- a.SendHandle("b_pipe", p0);
- b.SendHandle("a_pipe", p1);
-
- MojoHandle p2, p3;
-
- CreateMessagePipe(&p2, &p3);
- c.SendHandle("d_pipe", p2);
- d.SendHandle("c_pipe", p3);
-
- // Connect b to c via a and d
- MojoHandle p4, p5;
- CreateMessagePipe(&p4, &p5);
- a.SendHandle("d_pipe", p4);
- d.SendHandle("a_pipe", p5);
-
- // Have |a| pass its new |d|-pipe to |b|. It will eventually connect
- // to |c|.
- a.Send("pass:d_pipe:b_pipe");
- b.Send("catch:c_pipe:a_pipe");
-
- // Have |d| pass its new |a|-pipe to |c|. It will now be connected to
- // |b|.
- d.Send("pass:a_pipe:c_pipe");
- c.Send("catch:b_pipe:d_pipe");
-
- // Make sure b and c and talk.
- b.Send("say:c_pipe:it's a beautiful day");
- c.Send("hear:b_pipe:it's a beautiful day");
-
- // Create x and y and have b and c exchange them.
- MojoHandle x, y;
- CreateMessagePipe(&x, &y);
- b.SendHandle("x", x);
- c.SendHandle("y", y);
- b.Send("pass:x:c_pipe");
- c.Send("pass:y:b_pipe");
- b.Send("catch:y:c_pipe");
- c.Send("catch:x:b_pipe");
-
- // Make sure the pipe still works in both directions.
- b.Send("say:y:hello");
- c.Send("hear:x:hello");
- c.Send("say:x:goodbye");
- b.Send("hear:y:goodbye");
-
- // Take both pipes back.
- y = c.RetrieveHandle("x");
- x = b.RetrieveHandle("y");
-
- VerifyTransmission(x, y, "still works");
- VerifyTransmission(y, x, "in both directions");
-
- CloseHandle(x);
- CloseHandle(y);
-
- a.Exit();
- b.Exit();
- c.Exit();
- d.Exit();
- END_CHILD()
- END_CHILD()
- END_CHILD()
- END_CHILD()
-}
-
-DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReceivePipeWithClosedPeer,
- MultiprocessMessagePipeTest, h) {
- MojoHandle p;
- EXPECT_EQ("foo", ReadMessageWithHandles(h, &p, 1));
- EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
-}
-
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, SendPipeThenClosePeer) {
- RUN_CHILD_ON_PIPE(ReceivePipeWithClosedPeer, h)
- MojoHandle a, b;
- CreateMessagePipe(&a, &b);
-
- // Send |a| and immediately close |b|. The child should observe closure.
- WriteMessageWithHandles(h, "foo", &a, 1);
- MojoClose(b);
- END_CHILD()
-}
-
-DEFINE_TEST_CLIENT_TEST_WITH_PIPE(SendOtherChildPipeWithClosedPeer,
- MultiprocessMessagePipeTest, h) {
- // Create a new pipe and send one end to the parent, who will connect it to
- // a client running ReceivePipeWithClosedPeerFromOtherChild.
- MojoHandle application_proxy, application_request;
- CreateMessagePipe(&application_proxy, &application_request);
- WriteMessageWithHandles(h, "c2a plz", &application_request, 1);
-
- // Create another pipe and send one end to the remote "application".
- MojoHandle service_proxy, service_request;
- CreateMessagePipe(&service_proxy, &service_request);
- WriteMessageWithHandles(application_proxy, "c2s lol", &service_request, 1);
-
- // Immediately close the service proxy. The "application" should detect this.
- EXPECT_EQ(MOJO_RESULT_OK, MojoClose(service_proxy));
-
- // Wait for quit.
- EXPECT_EQ("quit", ReadMessage(h));
-}
-
-DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReceivePipeWithClosedPeerFromOtherChild,
- MultiprocessMessagePipeTest, h) {
- // Receive a pipe from the parent. This is akin to an "application request".
- MojoHandle application_client;
- EXPECT_EQ("c2a", ReadMessageWithHandles(h, &application_client, 1));
-
- // Receive a pipe from the "application" "client".
- MojoHandle service_client;
- EXPECT_EQ("c2s lol",
- ReadMessageWithHandles(application_client, &service_client, 1));
-
- // Wait for the service client to signal closure.
- EXPECT_EQ(MOJO_RESULT_OK,
- WaitForSignals(service_client, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
-
- EXPECT_EQ(MOJO_RESULT_OK, MojoClose(service_client));
- EXPECT_EQ(MOJO_RESULT_OK, MojoClose(application_client));
-}
-
-#if defined(OS_ANDROID)
-// Android multi-process tests are not executing the new process. This is flaky.
-#define MAYBE_SendPipeWithClosedPeerBetweenChildren \
- DISABLED_SendPipeWithClosedPeerBetweenChildren
-#else
-#define MAYBE_SendPipeWithClosedPeerBetweenChildren \
- SendPipeWithClosedPeerBetweenChildren
-#endif
-TEST_F(MultiprocessMessagePipeTest,
- MAYBE_SendPipeWithClosedPeerBetweenChildren) {
- RUN_CHILD_ON_PIPE(SendOtherChildPipeWithClosedPeer, kid_a)
- RUN_CHILD_ON_PIPE(ReceivePipeWithClosedPeerFromOtherChild, kid_b)
- // Receive an "application request" from the first child and forward it
- // to the second child.
- MojoHandle application_request;
- EXPECT_EQ("c2a plz",
- ReadMessageWithHandles(kid_a, &application_request, 1));
-
- WriteMessageWithHandles(kid_b, "c2a", &application_request, 1);
- END_CHILD()
-
- WriteMessage(kid_a, "quit");
- END_CHILD()
-}
-
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, SendClosePeerSend) {
- MojoHandle a, b;
- CreateMessagePipe(&a, &b);
-
- MojoHandle c, d;
- CreateMessagePipe(&c, &d);
-
- // Send |a| over |c|, immediately close |b|, then send |a| back over |d|.
- WriteMessageWithHandles(c, "foo", &a, 1);
- EXPECT_EQ("foo", ReadMessageWithHandles(d, &a, 1));
- WriteMessageWithHandles(d, "bar", &a, 1);
- EXPECT_EQ("bar", ReadMessageWithHandles(c, &a, 1));
- EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
-
- // We should be able to detect peer closure on |a|.
- EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(a, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
-}
-
-DEFINE_TEST_CLIENT_TEST_WITH_PIPE(WriteCloseSendPeerClient,
- MultiprocessMessagePipeTest, h) {
- MojoHandle pipe[2];
- EXPECT_EQ("foo", ReadMessageWithHandles(h, pipe, 2));
-
- // Write some messages to the first endpoint and then close it.
- WriteMessage(pipe[0], "baz");
- WriteMessage(pipe[0], "qux");
- EXPECT_EQ(MOJO_RESULT_OK, MojoClose(pipe[0]));
-
- MojoHandle c, d;
- CreateMessagePipe(&c, &d);
-
- // Pass the orphaned endpoint over another pipe before passing it back to
- // the parent, just for some extra proxying goodness.
- WriteMessageWithHandles(c, "foo", &pipe[1], 1);
- EXPECT_EQ("foo", ReadMessageWithHandles(d, &pipe[1], 1));
-
- // And finally pass it back to the parent.
- WriteMessageWithHandles(h, "bar", &pipe[1], 1);
-
- EXPECT_EQ("quit", ReadMessage(h));
-}
-
-TEST_P(MultiprocessMessagePipeTestWithPeerSupport, WriteCloseSendPeer) {
- MojoHandle pipe[2];
- CreateMessagePipe(&pipe[0], &pipe[1]);
-
- RUN_CHILD_ON_PIPE(WriteCloseSendPeerClient, h)
- // Pass the pipe to the child.
- WriteMessageWithHandles(h, "foo", pipe, 2);
-
- // Read back an endpoint which should have messages on it.
- MojoHandle p;
- EXPECT_EQ("bar", ReadMessageWithHandles(h, &p, 1));
-
- EXPECT_EQ("baz", ReadMessage(p));
- EXPECT_EQ("qux", ReadMessage(p));
-
- // Expect to have peer closure signaled.
- EXPECT_EQ(MOJO_RESULT_OK,
- WaitForSignals(p, MOJO_HANDLE_SIGNAL_PEER_CLOSED));
-
- WriteMessage(h, "quit");
- END_CHILD()
-}
-
-DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MessagePipeStatusChangeInTransitClient,
- MultiprocessMessagePipeTest, parent) {
- // This test verifies that peer closure is detectable through various
- // mechanisms when it races with handle transfer.
- MojoHandle handles[4];
- EXPECT_EQ("o_O", ReadMessageWithHandles(parent, handles, 4));
-
- EXPECT_EQ(MOJO_RESULT_OK,
- WaitForSignals(handles[0], MOJO_HANDLE_SIGNAL_PEER_CLOSED));
-
- base::MessageLoop message_loop;
-
- // Wait on handle 1 using a SimpleWatcher.
- {
- base::RunLoop run_loop;
- SimpleWatcher watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC);
- watcher.Watch(Handle(handles[1]), MOJO_HANDLE_SIGNAL_PEER_CLOSED,
- base::Bind(
- [](base::RunLoop* loop, MojoResult result) {
- EXPECT_EQ(MOJO_RESULT_OK, result);
- loop->Quit();
- },
- &run_loop));
- run_loop.Run();
- }
-
- // Wait on handle 2 by polling with MojoReadMessage.
- MojoResult result;
- do {
- result = MojoReadMessage(handles[2], nullptr, nullptr, nullptr, nullptr,
- MOJO_READ_MESSAGE_FLAG_NONE);
- } while (result == MOJO_RESULT_SHOULD_WAIT);
- EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
-
- // Wait on handle 3 by polling with MojoWriteMessage.
- do {
- result = MojoWriteMessage(handles[3], nullptr, 0, nullptr, 0,
- MOJO_WRITE_MESSAGE_FLAG_NONE);
- } while (result == MOJO_RESULT_OK);
- EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
-
- for (size_t i = 0; i < 4; ++i)
- CloseHandle(handles[i]);
-}
-
-TEST_F(MultiprocessMessagePipeTest, MessagePipeStatusChangeInTransit) {
- MojoHandle local_handles[4];
- MojoHandle sent_handles[4];
- for (size_t i = 0; i < 4; ++i)
- CreateMessagePipe(&local_handles[i], &sent_handles[i]);
-
- RUN_CHILD_ON_PIPE(MessagePipeStatusChangeInTransitClient, child)
- // Send 4 handles and let their transfer race with their peers' closure.
- WriteMessageWithHandles(child, "o_O", sent_handles, 4);
- for (size_t i = 0; i < 4; ++i)
- CloseHandle(local_handles[i]);
- END_CHILD()
-}
-
-DEFINE_TEST_CLIENT_TEST_WITH_PIPE(BadMessageClient, MultiprocessMessagePipeTest,
- parent) {
- MojoHandle pipe;
- EXPECT_EQ("hi", ReadMessageWithHandles(parent, &pipe, 1));
- WriteMessage(pipe, "derp");
- EXPECT_EQ("bye", ReadMessage(parent));
-}
-
-void OnProcessError(std::string* out_error, const std::string& error) {
- *out_error = error;
-}
-
-TEST_F(MultiprocessMessagePipeTest, NotifyBadMessage) {
- const std::string kFirstErrorMessage = "everything is terrible!";
- const std::string kSecondErrorMessage = "not the bits you're looking for";
-
- std::string first_process_error;
- std::string second_process_error;
-
- set_process_error_callback(base::Bind(&OnProcessError, &first_process_error));
- RUN_CHILD_ON_PIPE(BadMessageClient, child1)
- set_process_error_callback(base::Bind(&OnProcessError,
- &second_process_error));
- RUN_CHILD_ON_PIPE(BadMessageClient, child2)
- MojoHandle a, b, c, d;
- CreateMessagePipe(&a, &b);
- CreateMessagePipe(&c, &d);
- WriteMessageWithHandles(child1, "hi", &b, 1);
- WriteMessageWithHandles(child2, "hi", &d, 1);
-
- // Read a message from the pipe we sent to child1 and flag it as bad.
- ASSERT_EQ(MOJO_RESULT_OK, WaitForSignals(a, MOJO_HANDLE_SIGNAL_READABLE));
- uint32_t num_bytes = 0;
- MojoMessageHandle message;
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoReadMessageNew(a, &message, &num_bytes, nullptr, 0,
- MOJO_READ_MESSAGE_FLAG_NONE));
- EXPECT_EQ(MOJO_RESULT_OK,
- MojoNotifyBadMessage(message, kFirstErrorMessage.data(),
- kFirstErrorMessage.size()));
- EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message));
-
- // Read a message from the pipe we sent to child2 and flag it as bad.
- ASSERT_EQ(MOJO_RESULT_OK, WaitForSignals(c, MOJO_HANDLE_SIGNAL_READABLE));
- ASSERT_EQ(MOJO_RESULT_OK,
- MojoReadMessageNew(c, &message, &num_bytes, nullptr, 0,
- MOJO_READ_MESSAGE_FLAG_NONE));
- EXPECT_EQ(MOJO_RESULT_OK,
- MojoNotifyBadMessage(message, kSecondErrorMessage.data(),
- kSecondErrorMessage.size()));
- EXPECT_EQ(MOJO_RESULT_OK, MojoFreeMessage(message));
-
- WriteMessage(child2, "bye");
- END_CHILD();
-
- WriteMessage(child1, "bye");
- END_CHILD()
-
- // The error messages should match the processes which triggered them.
- EXPECT_NE(std::string::npos, first_process_error.find(kFirstErrorMessage));
- EXPECT_NE(std::string::npos, second_process_error.find(kSecondErrorMessage));
-}
-INSTANTIATE_TEST_CASE_P(
- ,
- MultiprocessMessagePipeTestWithPeerSupport,
- testing::Values(test::MojoTestBase::LaunchType::CHILD,
- test::MojoTestBase::LaunchType::PEER,
- test::MojoTestBase::LaunchType::NAMED_CHILD,
- test::MojoTestBase::LaunchType::NAMED_PEER));
-} // namespace
-} // namespace edk
-} // namespace mojo