|  | // 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 <string.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <vector> | 
|  |  | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/memory/ref_counted.h" | 
|  | #include "mojo/edk/system/test_utils.h" | 
|  | #include "mojo/edk/test/mojo_test_base.h" | 
|  | #include "mojo/public/c/system/core.h" | 
|  | #include "mojo/public/c/system/types.h" | 
|  | #include "mojo/public/cpp/system/message_pipe.h" | 
|  |  | 
|  | namespace mojo { | 
|  | namespace edk { | 
|  | namespace { | 
|  |  | 
|  | const MojoHandleSignals kAllSignals = | 
|  | MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE | | 
|  | MOJO_HANDLE_SIGNAL_PEER_CLOSED | MOJO_HANDLE_SIGNAL_PEER_REMOTE; | 
|  |  | 
|  | static const char kHelloWorld[] = "hello world"; | 
|  |  | 
|  | class MessagePipeTest : public test::MojoTestBase { | 
|  | public: | 
|  | MessagePipeTest() { | 
|  | CHECK_EQ(MOJO_RESULT_OK, MojoCreateMessagePipe(nullptr, &pipe0_, &pipe1_)); | 
|  | } | 
|  |  | 
|  | ~MessagePipeTest() override { | 
|  | if (pipe0_ != MOJO_HANDLE_INVALID) | 
|  | CHECK_EQ(MOJO_RESULT_OK, MojoClose(pipe0_)); | 
|  | if (pipe1_ != MOJO_HANDLE_INVALID) | 
|  | CHECK_EQ(MOJO_RESULT_OK, MojoClose(pipe1_)); | 
|  | } | 
|  |  | 
|  | MojoResult WriteMessage(MojoHandle message_pipe_handle, | 
|  | const void* bytes, | 
|  | uint32_t num_bytes) { | 
|  | return mojo::WriteMessageRaw(MessagePipeHandle(message_pipe_handle), bytes, | 
|  | num_bytes, nullptr, 0, | 
|  | MOJO_WRITE_MESSAGE_FLAG_NONE); | 
|  | } | 
|  |  | 
|  | MojoResult ReadMessage(MojoHandle message_pipe_handle, | 
|  | void* bytes, | 
|  | uint32_t* num_bytes, | 
|  | bool may_discard = false) { | 
|  | MojoMessageHandle message_handle; | 
|  | MojoResult rv = MojoReadMessage(message_pipe_handle, &message_handle, | 
|  | MOJO_READ_MESSAGE_FLAG_NONE); | 
|  | if (rv != MOJO_RESULT_OK) | 
|  | return rv; | 
|  |  | 
|  | const uint32_t expected_num_bytes = *num_bytes; | 
|  | void* buffer; | 
|  | rv = MojoGetSerializedMessageContents( | 
|  | message_handle, &buffer, num_bytes, nullptr, nullptr, | 
|  | MOJO_GET_SERIALIZED_MESSAGE_CONTENTS_FLAG_NONE); | 
|  |  | 
|  | if (rv == MOJO_RESULT_RESOURCE_EXHAUSTED) { | 
|  | CHECK(may_discard); | 
|  | } else if (*num_bytes) { | 
|  | CHECK_EQ(MOJO_RESULT_OK, rv); | 
|  | CHECK_GE(expected_num_bytes, *num_bytes); | 
|  | CHECK(bytes); | 
|  | memcpy(bytes, buffer, *num_bytes); | 
|  | } | 
|  | CHECK_EQ(MOJO_RESULT_OK, MojoDestroyMessage(message_handle)); | 
|  | return rv; | 
|  | } | 
|  |  | 
|  | MojoHandle pipe0_, pipe1_; | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(MessagePipeTest); | 
|  | }; | 
|  |  | 
|  | using FuseMessagePipeTest = test::MojoTestBase; | 
|  |  | 
|  | TEST_F(MessagePipeTest, WriteData) { | 
|  | ASSERT_EQ(MOJO_RESULT_OK, | 
|  | WriteMessage(pipe0_, kHelloWorld, sizeof(kHelloWorld))); | 
|  | } | 
|  |  | 
|  | // Tests: | 
|  | //  - only default flags | 
|  | //  - reading messages from a port | 
|  | //    - when there are no/one/two messages available for that port | 
|  | //    - with buffer size 0 (and null buffer) -- should get size | 
|  | //    - with too-small buffer -- should get size | 
|  | //    - also verify that buffers aren't modified when/where they shouldn't be | 
|  | //  - writing messages to a port | 
|  | //    - in the obvious scenarios (as above) | 
|  | //    - to a port that's been closed | 
|  | //  - writing a message to a port, closing the other (would be the source) port, | 
|  | //    and reading it | 
|  | TEST_F(MessagePipeTest, Basic) { | 
|  | int32_t buffer[2]; | 
|  | const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer)); | 
|  | uint32_t buffer_size; | 
|  |  | 
|  | // Nothing to read yet on port 0. | 
|  | buffer[0] = 123; | 
|  | buffer[1] = 456; | 
|  | buffer_size = kBufferSize; | 
|  | ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, ReadMessage(pipe0_, buffer, &buffer_size)); | 
|  | ASSERT_EQ(kBufferSize, buffer_size); | 
|  | ASSERT_EQ(123, buffer[0]); | 
|  | ASSERT_EQ(456, buffer[1]); | 
|  |  | 
|  | // Ditto for port 1. | 
|  | buffer[0] = 123; | 
|  | buffer[1] = 456; | 
|  | buffer_size = kBufferSize; | 
|  | ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, ReadMessage(pipe1_, buffer, &buffer_size)); | 
|  |  | 
|  | // Write from port 1 (to port 0). | 
|  | buffer[0] = 789012345; | 
|  | buffer[1] = 0; | 
|  | ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, sizeof(buffer[0]))); | 
|  |  | 
|  | MojoHandleSignalsState state; | 
|  | ASSERT_EQ(MOJO_RESULT_OK, | 
|  | WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state)); | 
|  |  | 
|  | // Read from port 0. | 
|  | buffer[0] = 123; | 
|  | buffer[1] = 456; | 
|  | buffer_size = kBufferSize; | 
|  | ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe0_, buffer, &buffer_size)); | 
|  | ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); | 
|  | ASSERT_EQ(789012345, buffer[0]); | 
|  | ASSERT_EQ(456, buffer[1]); | 
|  |  | 
|  | // Read again from port 0 -- it should be empty. | 
|  | buffer_size = kBufferSize; | 
|  | ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, ReadMessage(pipe0_, buffer, &buffer_size)); | 
|  |  | 
|  | // Write two messages from port 0 (to port 1). | 
|  | buffer[0] = 123456789; | 
|  | buffer[1] = 0; | 
|  | ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe0_, buffer, sizeof(buffer[0]))); | 
|  | buffer[0] = 234567890; | 
|  | buffer[1] = 0; | 
|  | ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe0_, buffer, sizeof(buffer[0]))); | 
|  |  | 
|  | ASSERT_EQ(MOJO_RESULT_OK, | 
|  | WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &state)); | 
|  |  | 
|  | // Read from port 1. | 
|  | buffer[0] = 123; | 
|  | buffer[1] = 456; | 
|  | buffer_size = kBufferSize; | 
|  | ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe1_, buffer, &buffer_size)); | 
|  | ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); | 
|  | ASSERT_EQ(123456789, buffer[0]); | 
|  | ASSERT_EQ(456, buffer[1]); | 
|  |  | 
|  | ASSERT_EQ(MOJO_RESULT_OK, | 
|  | WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &state)); | 
|  |  | 
|  | // Read again from port 1. | 
|  | buffer[0] = 123; | 
|  | buffer[1] = 456; | 
|  | buffer_size = kBufferSize; | 
|  | ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe1_, buffer, &buffer_size)); | 
|  | ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); | 
|  | ASSERT_EQ(234567890, buffer[0]); | 
|  | ASSERT_EQ(456, buffer[1]); | 
|  |  | 
|  | // Read again from port 1 -- it should be empty. | 
|  | buffer_size = kBufferSize; | 
|  | ASSERT_EQ(MOJO_RESULT_SHOULD_WAIT, ReadMessage(pipe1_, buffer, &buffer_size)); | 
|  |  | 
|  | // Write from port 0 (to port 1). | 
|  | buffer[0] = 345678901; | 
|  | buffer[1] = 0; | 
|  | ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe0_, buffer, sizeof(buffer[0]))); | 
|  |  | 
|  | // Close port 0. | 
|  | MojoClose(pipe0_); | 
|  | pipe0_ = MOJO_HANDLE_INVALID; | 
|  |  | 
|  | ASSERT_EQ(MOJO_RESULT_OK, | 
|  | WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &state)); | 
|  |  | 
|  | // Try to write from port 1 (to port 0). | 
|  | buffer[0] = 456789012; | 
|  | buffer[1] = 0; | 
|  | ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, | 
|  | WriteMessage(pipe1_, buffer, sizeof(buffer[0]))); | 
|  |  | 
|  | // Read from port 1; should still get message (even though port 0 was closed). | 
|  | buffer[0] = 123; | 
|  | buffer[1] = 456; | 
|  | buffer_size = kBufferSize; | 
|  | ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe1_, buffer, &buffer_size)); | 
|  | ASSERT_EQ(static_cast<uint32_t>(sizeof(buffer[0])), buffer_size); | 
|  | ASSERT_EQ(345678901, buffer[0]); | 
|  | ASSERT_EQ(456, buffer[1]); | 
|  |  | 
|  | // Read again from port 1 -- it should be empty (and port 0 is closed). | 
|  | buffer_size = kBufferSize; | 
|  | ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, | 
|  | ReadMessage(pipe1_, buffer, &buffer_size)); | 
|  | } | 
|  |  | 
|  | TEST_F(MessagePipeTest, CloseWithQueuedIncomingMessages) { | 
|  | int32_t buffer[1]; | 
|  | const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer)); | 
|  | uint32_t buffer_size; | 
|  |  | 
|  | // Write some messages from port 1 (to port 0). | 
|  | for (int32_t i = 0; i < 5; i++) { | 
|  | buffer[0] = i; | 
|  | ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe1_, buffer, kBufferSize)); | 
|  | } | 
|  |  | 
|  | MojoHandleSignalsState state; | 
|  | ASSERT_EQ(MOJO_RESULT_OK, | 
|  | WaitForSignals(pipe0_, MOJO_HANDLE_SIGNAL_READABLE, &state)); | 
|  |  | 
|  | // Port 0 shouldn't be empty. | 
|  | buffer_size = kBufferSize; | 
|  | ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe0_, buffer, &buffer_size)); | 
|  | ASSERT_EQ(kBufferSize, buffer_size); | 
|  |  | 
|  | // Close port 0 first, which should have outstanding (incoming) messages. | 
|  | MojoClose(pipe0_); | 
|  | MojoClose(pipe1_); | 
|  | pipe0_ = pipe1_ = MOJO_HANDLE_INVALID; | 
|  | } | 
|  |  | 
|  | TEST_F(MessagePipeTest, BasicWaiting) { | 
|  | MojoHandleSignalsState hss; | 
|  |  | 
|  | int32_t buffer[1]; | 
|  | const uint32_t kBufferSize = static_cast<uint32_t>(sizeof(buffer)); | 
|  | uint32_t buffer_size; | 
|  |  | 
|  | // Always writable (until the other port is closed). Not yet readable. Peer | 
|  | // not closed. | 
|  | hss = GetSignalsState(pipe0_); | 
|  | ASSERT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE, hss.satisfied_signals); | 
|  | ASSERT_EQ(kAllSignals, hss.satisfiable_signals); | 
|  | hss = MojoHandleSignalsState(); | 
|  |  | 
|  | // Write from port 0 (to port 1), to make port 1 readable. | 
|  | buffer[0] = 123456789; | 
|  | ASSERT_EQ(MOJO_RESULT_OK, WriteMessage(pipe0_, buffer, kBufferSize)); | 
|  |  | 
|  | // Port 1 should already be readable now. | 
|  | ASSERT_EQ(MOJO_RESULT_OK, | 
|  | WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); | 
|  | ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, | 
|  | hss.satisfied_signals); | 
|  | ASSERT_EQ(kAllSignals, hss.satisfiable_signals); | 
|  | // ... and still writable. | 
|  | hss = MojoHandleSignalsState(); | 
|  | ASSERT_EQ(MOJO_RESULT_OK, | 
|  | WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_WRITABLE, &hss)); | 
|  | ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE, | 
|  | hss.satisfied_signals); | 
|  | ASSERT_EQ(kAllSignals, hss.satisfiable_signals); | 
|  |  | 
|  | // Close port 0. | 
|  | MojoClose(pipe0_); | 
|  | pipe0_ = MOJO_HANDLE_INVALID; | 
|  |  | 
|  | // Port 1 should be signaled with peer closed. | 
|  | hss = MojoHandleSignalsState(); | 
|  | ASSERT_EQ(MOJO_RESULT_OK, | 
|  | WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_PEER_CLOSED, &hss)); | 
|  | ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, | 
|  | hss.satisfied_signals); | 
|  | ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, | 
|  | hss.satisfiable_signals); | 
|  |  | 
|  | // Port 1 should not be writable. | 
|  | hss = MojoHandleSignalsState(); | 
|  |  | 
|  | ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, | 
|  | WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_WRITABLE, &hss)); | 
|  | ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, | 
|  | hss.satisfied_signals); | 
|  | ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, | 
|  | hss.satisfiable_signals); | 
|  |  | 
|  | // But it should still be readable. | 
|  | hss = MojoHandleSignalsState(); | 
|  | ASSERT_EQ(MOJO_RESULT_OK, | 
|  | WaitForSignals(pipe1_, MOJO_HANDLE_SIGNAL_READABLE, &hss)); | 
|  | ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, | 
|  | hss.satisfied_signals); | 
|  | ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED, | 
|  | hss.satisfiable_signals); | 
|  |  | 
|  | // Read from port 1. | 
|  | buffer[0] = 0; | 
|  | buffer_size = kBufferSize; | 
|  | ASSERT_EQ(MOJO_RESULT_OK, ReadMessage(pipe1_, buffer, &buffer_size)); | 
|  | ASSERT_EQ(123456789, buffer[0]); | 
|  |  | 
|  | // Now port 1 should no longer be readable. | 
|  | hss = MojoHandleSignalsState(); | 
|  | ASSERT_EQ(MOJO_RESULT_FAILED_PRECONDITION, | 
|  | WaitForSignals(pipe1_, 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); | 
|  | } | 
|  |  | 
|  | #if !defined(OS_IOS) | 
|  |  | 
|  | const size_t kPingPongHandlesPerIteration = 50; | 
|  | const size_t kPingPongIterations = 500; | 
|  |  | 
|  | DEFINE_TEST_CLIENT_TEST_WITH_PIPE(HandlePingPong, MessagePipeTest, h) { | 
|  | // Waits for a handle to become readable and writes it back to the sender. | 
|  | for (size_t i = 0; i < kPingPongIterations; i++) { | 
|  | MojoHandle handles[kPingPongHandlesPerIteration]; | 
|  | ReadMessageWithHandles(h, handles, kPingPongHandlesPerIteration); | 
|  | WriteMessageWithHandles(h, "", handles, kPingPongHandlesPerIteration); | 
|  | } | 
|  |  | 
|  | EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(h, MOJO_HANDLE_SIGNAL_READABLE)); | 
|  | char msg[4]; | 
|  | uint32_t num_bytes = 4; | 
|  | EXPECT_EQ(MOJO_RESULT_OK, ReadMessage(h, msg, &num_bytes)); | 
|  | } | 
|  |  | 
|  | // This test is flaky: http://crbug.com/585784 | 
|  | TEST_F(MessagePipeTest, DISABLED_DataPipeConsumerHandlePingPong) { | 
|  | MojoHandle p, c[kPingPongHandlesPerIteration]; | 
|  | for (size_t i = 0; i < kPingPongHandlesPerIteration; ++i) { | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(nullptr, &p, &c[i])); | 
|  | MojoClose(p); | 
|  | } | 
|  |  | 
|  | RunTestClient("HandlePingPong", [&](MojoHandle h) { | 
|  | for (size_t i = 0; i < kPingPongIterations; i++) { | 
|  | WriteMessageWithHandles(h, "", c, kPingPongHandlesPerIteration); | 
|  | ReadMessageWithHandles(h, c, kPingPongHandlesPerIteration); | 
|  | } | 
|  | WriteMessage(h, "quit", 4); | 
|  | }); | 
|  | for (size_t i = 0; i < kPingPongHandlesPerIteration; ++i) | 
|  | MojoClose(c[i]); | 
|  | } | 
|  |  | 
|  | // This test is flaky: http://crbug.com/585784 | 
|  | TEST_F(MessagePipeTest, DISABLED_DataPipeProducerHandlePingPong) { | 
|  | MojoHandle p[kPingPongHandlesPerIteration], c; | 
|  | for (size_t i = 0; i < kPingPongHandlesPerIteration; ++i) { | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(nullptr, &p[i], &c)); | 
|  | MojoClose(c); | 
|  | } | 
|  |  | 
|  | RunTestClient("HandlePingPong", [&](MojoHandle h) { | 
|  | for (size_t i = 0; i < kPingPongIterations; i++) { | 
|  | WriteMessageWithHandles(h, "", p, kPingPongHandlesPerIteration); | 
|  | ReadMessageWithHandles(h, p, kPingPongHandlesPerIteration); | 
|  | } | 
|  | WriteMessage(h, "quit", 4); | 
|  | }); | 
|  | for (size_t i = 0; i < kPingPongHandlesPerIteration; ++i) | 
|  | MojoClose(p[i]); | 
|  | } | 
|  |  | 
|  | TEST_F(MessagePipeTest, SharedBufferHandlePingPong) { | 
|  | MojoHandle buffers[kPingPongHandlesPerIteration]; | 
|  | for (size_t i = 0; i < kPingPongHandlesPerIteration; ++i) | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoCreateSharedBuffer(nullptr, 1, &buffers[i])); | 
|  |  | 
|  | RunTestClient("HandlePingPong", [&](MojoHandle h) { | 
|  | for (size_t i = 0; i < kPingPongIterations; i++) { | 
|  | WriteMessageWithHandles(h, "", buffers, kPingPongHandlesPerIteration); | 
|  | ReadMessageWithHandles(h, buffers, kPingPongHandlesPerIteration); | 
|  | } | 
|  | WriteMessage(h, "quit", 4); | 
|  | }); | 
|  | for (size_t i = 0; i < kPingPongHandlesPerIteration; ++i) | 
|  | MojoClose(buffers[i]); | 
|  | } | 
|  |  | 
|  | #endif  // !defined(OS_IOS) | 
|  |  | 
|  | TEST_F(FuseMessagePipeTest, Basic) { | 
|  | // Test that we can fuse pipes and they still work. | 
|  |  | 
|  | MojoHandle a, b, c, d; | 
|  | CreateMessagePipe(&a, &b); | 
|  | CreateMessagePipe(&c, &d); | 
|  |  | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoFuseMessagePipes(b, c)); | 
|  |  | 
|  | // Handles b and c should be closed. | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b)); | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c)); | 
|  |  | 
|  | const std::string kTestMessage1 = "Hello, world!"; | 
|  | const std::string kTestMessage2 = "Goodbye, world!"; | 
|  |  | 
|  | WriteMessage(a, kTestMessage1); | 
|  | EXPECT_EQ(kTestMessage1, ReadMessage(d)); | 
|  |  | 
|  | WriteMessage(d, kTestMessage2); | 
|  | EXPECT_EQ(kTestMessage2, ReadMessage(a)); | 
|  |  | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); | 
|  | } | 
|  |  | 
|  | TEST_F(FuseMessagePipeTest, FuseAfterPeerWrite) { | 
|  | // Test that messages written before fusion are eventually delivered. | 
|  |  | 
|  | MojoHandle a, b, c, d; | 
|  | CreateMessagePipe(&a, &b); | 
|  | CreateMessagePipe(&c, &d); | 
|  |  | 
|  | const std::string kTestMessage1 = "Hello, world!"; | 
|  | const std::string kTestMessage2 = "Goodbye, world!"; | 
|  | WriteMessage(a, kTestMessage1); | 
|  | WriteMessage(d, kTestMessage2); | 
|  |  | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoFuseMessagePipes(b, c)); | 
|  |  | 
|  | // Handles b and c should be closed. | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b)); | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c)); | 
|  |  | 
|  | EXPECT_EQ(kTestMessage1, ReadMessage(d)); | 
|  | EXPECT_EQ(kTestMessage2, ReadMessage(a)); | 
|  |  | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); | 
|  | } | 
|  |  | 
|  | TEST_F(FuseMessagePipeTest, NoFuseAfterWrite) { | 
|  | // Test that a pipe endpoint which has been written to cannot be fused. | 
|  |  | 
|  | MojoHandle a, b, c, d; | 
|  | CreateMessagePipe(&a, &b); | 
|  | CreateMessagePipe(&c, &d); | 
|  |  | 
|  | WriteMessage(b, "shouldn't have done that!"); | 
|  | EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoFuseMessagePipes(b, c)); | 
|  |  | 
|  | // Handles b and c should be closed. | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b)); | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c)); | 
|  |  | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); | 
|  | } | 
|  |  | 
|  | TEST_F(FuseMessagePipeTest, NoFuseSelf) { | 
|  | // Test that a pipe's own endpoints can't be fused together. | 
|  |  | 
|  | MojoHandle a, b; | 
|  | CreateMessagePipe(&a, &b); | 
|  |  | 
|  | EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoFuseMessagePipes(a, b)); | 
|  |  | 
|  | // Handles a and b should be closed. | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(a)); | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b)); | 
|  | } | 
|  |  | 
|  | TEST_F(FuseMessagePipeTest, FuseInvalidArguments) { | 
|  | MojoHandle a, b, c, d; | 
|  | CreateMessagePipe(&a, &b); | 
|  | CreateMessagePipe(&c, &d); | 
|  |  | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); | 
|  |  | 
|  | // Can't fuse an invalid handle. | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoFuseMessagePipes(b, c)); | 
|  |  | 
|  | // Handle c should be closed. | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c)); | 
|  |  | 
|  | // Can't fuse a non-message pipe handle. | 
|  | MojoHandle e, f; | 
|  | CreateDataPipe(&e, &f, 16); | 
|  |  | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoFuseMessagePipes(e, d)); | 
|  |  | 
|  | // Handles d and e should be closed. | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(d)); | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(e)); | 
|  |  | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoClose(f)); | 
|  | } | 
|  |  | 
|  | TEST_F(FuseMessagePipeTest, FuseAfterPeerClosure) { | 
|  | // Test that peer closure prior to fusion can still be detected after fusion. | 
|  |  | 
|  | MojoHandle a, b, c, d; | 
|  | CreateMessagePipe(&a, &b); | 
|  | CreateMessagePipe(&c, &d); | 
|  |  | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoFuseMessagePipes(b, c)); | 
|  |  | 
|  | // Handles b and c should be closed. | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b)); | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c)); | 
|  |  | 
|  | EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(d, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); | 
|  | } | 
|  |  | 
|  | TEST_F(FuseMessagePipeTest, FuseAfterPeerWriteAndClosure) { | 
|  | // Test that peer write and closure prior to fusion still results in the | 
|  | // both message arrival and awareness of peer closure. | 
|  |  | 
|  | MojoHandle a, b, c, d; | 
|  | CreateMessagePipe(&a, &b); | 
|  | CreateMessagePipe(&c, &d); | 
|  |  | 
|  | const std::string kTestMessage = "ayyy lmao"; | 
|  | WriteMessage(a, kTestMessage); | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); | 
|  |  | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoFuseMessagePipes(b, c)); | 
|  |  | 
|  | // Handles b and c should be closed. | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(b)); | 
|  | EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoClose(c)); | 
|  |  | 
|  | EXPECT_EQ(kTestMessage, ReadMessage(d)); | 
|  | EXPECT_EQ(MOJO_RESULT_OK, WaitForSignals(d, MOJO_HANDLE_SIGNAL_PEER_CLOSED)); | 
|  | EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); | 
|  | } | 
|  |  | 
|  | TEST_F(MessagePipeTest, ClosePipesStressTest) { | 
|  | // Stress test to exercise https://crbug.com/665869. | 
|  | const size_t kNumPipes = 100000; | 
|  | for (size_t i = 0; i < kNumPipes; ++i) { | 
|  | MojoHandle a, b; | 
|  | CreateMessagePipe(&a, &b); | 
|  | MojoClose(a); | 
|  | MojoClose(b); | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace edk | 
|  | }  // namespace mojo |