| Index: mojo/public/cpp/system/tests/wait_set_unittest.cc
|
| diff --git a/mojo/public/cpp/system/tests/wait_set_unittest.cc b/mojo/public/cpp/system/tests/wait_set_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2a6d9b88256b32e9857971e6d2037454d3c6077f
|
| --- /dev/null
|
| +++ b/mojo/public/cpp/system/tests/wait_set_unittest.cc
|
| @@ -0,0 +1,260 @@
|
| +// Copyright 2017 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 "mojo/public/cpp/system/wait_set.h"
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/callback.h"
|
| +#include "base/threading/platform_thread.h"
|
| +#include "base/threading/simple_thread.h"
|
| +#include "mojo/public/cpp/system/message_pipe.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace mojo {
|
| +namespace {
|
| +
|
| +using WaitSetTest = testing::Test;
|
| +
|
| +void WriteMessage(const ScopedMessagePipeHandle& handle,
|
| + const base::StringPiece& message) {
|
| + MojoResult rv = WriteMessageRaw(handle.get(), message.data(),
|
| + static_cast<uint32_t>(message.size()),
|
| + nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE);
|
| + CHECK_EQ(MOJO_RESULT_OK, rv);
|
| +}
|
| +
|
| +std::string ReadMessage(const ScopedMessagePipeHandle& handle) {
|
| + uint32_t num_bytes = 0;
|
| + uint32_t num_handles = 0;
|
| + MojoResult rv = ReadMessageRaw(handle.get(), nullptr, &num_bytes, nullptr,
|
| + &num_handles, MOJO_READ_MESSAGE_FLAG_NONE);
|
| + CHECK_EQ(MOJO_RESULT_RESOURCE_EXHAUSTED, rv);
|
| + CHECK_EQ(0u, num_handles);
|
| +
|
| + std::vector<char> buffer(num_bytes);
|
| + rv = ReadMessageRaw(handle.get(), buffer.data(), &num_bytes, nullptr,
|
| + &num_handles, MOJO_READ_MESSAGE_FLAG_NONE);
|
| + CHECK_EQ(MOJO_RESULT_OK, rv);
|
| + return std::string(buffer.data(), buffer.size());
|
| +}
|
| +
|
| +class ThreadedRunner : public base::SimpleThread {
|
| + public:
|
| + explicit ThreadedRunner(const base::Closure& callback)
|
| + : SimpleThread("ThreadedRunner"), callback_(callback) {}
|
| + ~ThreadedRunner() override { Join(); }
|
| +
|
| + void Run() override { callback_.Run(); }
|
| +
|
| + private:
|
| + const base::Closure callback_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(ThreadedRunner);
|
| +};
|
| +
|
| +TEST_F(WaitSetTest, Satisfied) {
|
| + WaitSet wait_set;
|
| + MessagePipe p;
|
| +
|
| + const char kTestMessage1[] = "hello wake up";
|
| +
|
| + // Watch only one handle and write to the other.
|
| +
|
| + wait_set.AddHandle(p.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE);
|
| + WriteMessage(p.handle0, kTestMessage1);
|
| +
|
| + size_t num_ready_handles = 2;
|
| + Handle ready_handles[2];
|
| + MojoResult ready_results[2] = {MOJO_RESULT_UNKNOWN, MOJO_RESULT_UNKNOWN};
|
| + HandleSignalsState hss[2];
|
| + wait_set.Wait(&num_ready_handles, ready_handles, ready_results, hss);
|
| +
|
| + EXPECT_EQ(1u, num_ready_handles);
|
| + EXPECT_EQ(p.handle1.get(), ready_handles[0]);
|
| + EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
|
| + EXPECT_TRUE(hss[0].readable() && hss[0].writable() && !hss[0].peer_closed());
|
| +
|
| + wait_set.RemoveHandle(p.handle1.get());
|
| +
|
| + // Now watch only the other handle and write to the first one.
|
| +
|
| + wait_set.AddHandle(p.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
|
| + WriteMessage(p.handle1, kTestMessage1);
|
| +
|
| + num_ready_handles = 2;
|
| + ready_results[0] = MOJO_RESULT_UNKNOWN;
|
| + ready_results[1] = MOJO_RESULT_UNKNOWN;
|
| + wait_set.Wait(&num_ready_handles, ready_handles, ready_results, hss);
|
| +
|
| + EXPECT_EQ(1u, num_ready_handles);
|
| + EXPECT_EQ(p.handle0.get(), ready_handles[0]);
|
| + EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
|
| + EXPECT_TRUE(hss[0].readable() && hss[0].writable() && !hss[0].peer_closed());
|
| +
|
| + // Now wait on both of them.
|
| + wait_set.AddHandle(p.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE);
|
| +
|
| + num_ready_handles = 2;
|
| + ready_results[0] = MOJO_RESULT_UNKNOWN;
|
| + ready_results[1] = MOJO_RESULT_UNKNOWN;
|
| + wait_set.Wait(&num_ready_handles, ready_handles, ready_results, hss);
|
| + EXPECT_EQ(2u, num_ready_handles);
|
| + EXPECT_TRUE((ready_handles[0] == p.handle0.get() &&
|
| + ready_handles[1] == p.handle1.get()) ||
|
| + (ready_handles[0] == p.handle1.get() &&
|
| + ready_handles[1] == p.handle0.get()));
|
| + EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
|
| + EXPECT_EQ(MOJO_RESULT_OK, ready_results[1]);
|
| + EXPECT_TRUE(hss[0].readable() && hss[0].writable() && !hss[0].peer_closed());
|
| + EXPECT_TRUE(hss[1].readable() && hss[1].writable() && !hss[1].peer_closed());
|
| +
|
| + // Wait on both again, but with only enough output space for one result.
|
| + num_ready_handles = 1;
|
| + ready_results[0] = MOJO_RESULT_UNKNOWN;
|
| + wait_set.Wait(&num_ready_handles, ready_handles, ready_results, hss);
|
| + EXPECT_EQ(1u, num_ready_handles);
|
| + EXPECT_TRUE(ready_handles[0] == p.handle0.get() ||
|
| + ready_handles[0] == p.handle1.get());
|
| + EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
|
| +
|
| + // Remove the ready handle from the set and wait one more time.
|
| + EXPECT_EQ(MOJO_RESULT_OK, wait_set.RemoveHandle(ready_handles[0]));
|
| +
|
| + num_ready_handles = 1;
|
| + ready_results[0] = MOJO_RESULT_UNKNOWN;
|
| + wait_set.Wait(&num_ready_handles, ready_handles, ready_results, hss);
|
| + EXPECT_EQ(1u, num_ready_handles);
|
| + EXPECT_TRUE(ready_handles[0] == p.handle0.get() ||
|
| + ready_handles[0] == p.handle1.get());
|
| + EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
|
| +
|
| + EXPECT_EQ(MOJO_RESULT_OK, wait_set.RemoveHandle(ready_handles[0]));
|
| +
|
| + // The wait set should be empty now. Nothing to wait on.
|
| + num_ready_handles = 2;
|
| + wait_set.Wait(&num_ready_handles, ready_handles, ready_results);
|
| + EXPECT_EQ(0u, num_ready_handles);
|
| +}
|
| +
|
| +TEST_F(WaitSetTest, Unsatisfiable) {
|
| + MessagePipe p, q;
|
| + WaitSet wait_set;
|
| +
|
| + wait_set.AddHandle(q.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
|
| + wait_set.AddHandle(q.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE);
|
| + wait_set.AddHandle(p.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
|
| +
|
| + size_t num_ready_handles = 2;
|
| + Handle ready_handles[2];
|
| + MojoResult ready_results[2] = {MOJO_RESULT_UNKNOWN, MOJO_RESULT_UNKNOWN};
|
| +
|
| + p.handle1.reset();
|
| + wait_set.Wait(&num_ready_handles, ready_handles, ready_results);
|
| + EXPECT_EQ(1u, num_ready_handles);
|
| + EXPECT_EQ(p.handle0.get(), ready_handles[0]);
|
| + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, ready_results[0]);
|
| +}
|
| +
|
| +TEST_F(WaitSetTest, CloseWhileWaiting) {
|
| + MessagePipe p;
|
| + WaitSet wait_set;
|
| +
|
| + wait_set.AddHandle(p.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
|
| +
|
| + const Handle handle0_value = p.handle0.get();
|
| + ThreadedRunner close_after_delay(base::Bind(
|
| + [](ScopedMessagePipeHandle* handle) {
|
| + // Wait a little while, then close the handle.
|
| + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
|
| + handle->reset();
|
| + },
|
| + &p.handle0));
|
| + close_after_delay.Start();
|
| +
|
| + size_t num_ready_handles = 2;
|
| + Handle ready_handles[2];
|
| + MojoResult ready_results[2] = {MOJO_RESULT_UNKNOWN, MOJO_RESULT_UNKNOWN};
|
| + wait_set.Wait(&num_ready_handles, ready_handles, ready_results);
|
| + EXPECT_EQ(1u, num_ready_handles);
|
| + EXPECT_EQ(handle0_value, ready_handles[0]);
|
| + EXPECT_EQ(MOJO_RESULT_CANCELLED, ready_results[0]);
|
| +
|
| + EXPECT_EQ(MOJO_RESULT_NOT_FOUND, wait_set.RemoveHandle(handle0_value));
|
| +}
|
| +
|
| +TEST_F(WaitSetTest, CloseBeforeWaiting) {
|
| + MessagePipe p;
|
| + WaitSet wait_set;
|
| +
|
| + wait_set.AddHandle(p.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
|
| + wait_set.AddHandle(p.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE);
|
| +
|
| + Handle handle0_value = p.handle0.get();
|
| + Handle handle1_value = p.handle1.get();
|
| +
|
| + p.handle0.reset();
|
| + p.handle1.reset();
|
| +
|
| + // Ensure that the WaitSet user is always made aware of all cancellations even
|
| + // if they happen while not waiting, or they have to be returned over the span
|
| + // of multiple Wait() calls due to insufficient output storage.
|
| +
|
| + size_t num_ready_handles = 1;
|
| + Handle ready_handle;
|
| + MojoResult ready_result = MOJO_RESULT_UNKNOWN;
|
| + wait_set.Wait(&num_ready_handles, &ready_handle, &ready_result);
|
| + EXPECT_EQ(1u, num_ready_handles);
|
| + EXPECT_TRUE(ready_handle == handle0_value || ready_handle == handle1_value);
|
| + EXPECT_EQ(MOJO_RESULT_CANCELLED, ready_result);
|
| + EXPECT_EQ(MOJO_RESULT_NOT_FOUND, wait_set.RemoveHandle(handle0_value));
|
| +
|
| + wait_set.Wait(&num_ready_handles, &ready_handle, &ready_result);
|
| + EXPECT_EQ(1u, num_ready_handles);
|
| + EXPECT_TRUE(ready_handle == handle0_value || ready_handle == handle1_value);
|
| + EXPECT_EQ(MOJO_RESULT_CANCELLED, ready_result);
|
| + EXPECT_EQ(MOJO_RESULT_NOT_FOUND, wait_set.RemoveHandle(handle0_value));
|
| +
|
| + // Nothing more to wait on.
|
| + wait_set.Wait(&num_ready_handles, &ready_handle, &ready_result);
|
| + EXPECT_EQ(0u, num_ready_handles);
|
| +}
|
| +
|
| +TEST_F(WaitSetTest, SatisfiedThenUnsatisfied) {
|
| + MessagePipe p;
|
| + WaitSet wait_set;
|
| +
|
| + wait_set.AddHandle(p.handle0.get(), MOJO_HANDLE_SIGNAL_READABLE);
|
| + wait_set.AddHandle(p.handle1.get(), MOJO_HANDLE_SIGNAL_READABLE);
|
| +
|
| + const char kTestMessage1[] = "testing testing testing";
|
| + WriteMessage(p.handle0, kTestMessage1);
|
| +
|
| + size_t num_ready_handles = 2;
|
| + Handle ready_handles[2];
|
| + MojoResult ready_results[2] = {MOJO_RESULT_UNKNOWN, MOJO_RESULT_UNKNOWN};
|
| + wait_set.Wait(&num_ready_handles, ready_handles, ready_results);
|
| + EXPECT_EQ(1u, num_ready_handles);
|
| + EXPECT_EQ(p.handle1.get(), ready_handles[0]);
|
| + EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
|
| +
|
| + EXPECT_EQ(kTestMessage1, ReadMessage(p.handle1));
|
| +
|
| + ThreadedRunner write_after_delay(base::Bind(
|
| + [](ScopedMessagePipeHandle* handle) {
|
| + // Wait a little while, then write a message.
|
| + base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
|
| + WriteMessage(*handle, "wakey wakey");
|
| + },
|
| + &p.handle1));
|
| + write_after_delay.Start();
|
| +
|
| + num_ready_handles = 2;
|
| + wait_set.Wait(&num_ready_handles, ready_handles, ready_results);
|
| + EXPECT_EQ(1u, num_ready_handles);
|
| + EXPECT_EQ(p.handle0.get(), ready_handles[0]);
|
| + EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]);
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace mojo
|
|
|