| Index: mojo/edk/system/watch_unittest.cc
|
| diff --git a/mojo/edk/system/watch_unittest.cc b/mojo/edk/system/watch_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..fd0c13d88cb9e1d0a43821edbb9dde361549cbfd
|
| --- /dev/null
|
| +++ b/mojo/edk/system/watch_unittest.cc
|
| @@ -0,0 +1,374 @@
|
| +// Copyright 2016 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 <functional>
|
| +
|
| +#include "base/macros.h"
|
| +#include "base/message_loop/message_loop.h"
|
| +#include "base/run_loop.h"
|
| +#include "mojo/edk/test/mojo_test_base.h"
|
| +#include "mojo/public/c/system/functions.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace mojo {
|
| +namespace edk {
|
| +namespace {
|
| +
|
| +void IgnoreResult(uintptr_t context,
|
| + MojoResult result,
|
| + MojoHandleSignalsState signals) {
|
| +}
|
| +
|
| +// A test helper class for watching a handle. The WatchHelper instance is used
|
| +// as a watch context for a single watch callback.
|
| +class WatchHelper {
|
| + public:
|
| + using Callback =
|
| + std::function<void(MojoResult result, MojoHandleSignalsState state)>;
|
| +
|
| + WatchHelper() {}
|
| + ~WatchHelper() {
|
| + CHECK(!watching_);
|
| + }
|
| +
|
| + void Watch(MojoHandle handle,
|
| + MojoHandleSignals signals,
|
| + const Callback& callback) {
|
| + CHECK(!watching_);
|
| +
|
| + handle_ = handle;
|
| + callback_ = callback;
|
| + watching_ = true;
|
| + CHECK_EQ(MOJO_RESULT_OK, MojoWatch(handle_, signals, &WatchHelper::OnNotify,
|
| + reinterpret_cast<uintptr_t>(this)));
|
| + }
|
| +
|
| + bool is_watching() const { return watching_; }
|
| +
|
| + void Cancel() {
|
| + CHECK_EQ(MOJO_RESULT_OK,
|
| + MojoCancelWatch(handle_, reinterpret_cast<uintptr_t>(this)));
|
| + CHECK(watching_);
|
| + watching_ = false;
|
| + }
|
| +
|
| + private:
|
| + static void OnNotify(uintptr_t context,
|
| + MojoResult result,
|
| + MojoHandleSignalsState state) {
|
| + WatchHelper* watcher = reinterpret_cast<WatchHelper*>(context);
|
| + CHECK(watcher->watching_);
|
| + if (result == MOJO_RESULT_CANCELLED)
|
| + watcher->watching_ = false;
|
| + watcher->callback_(result, state);
|
| + }
|
| +
|
| + bool watching_ = false;
|
| + MojoHandle handle_;
|
| + Callback callback_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(WatchHelper);
|
| +};
|
| +
|
| +class WatchTest : public test::MojoTestBase {
|
| + public:
|
| + WatchTest() {}
|
| + ~WatchTest() override {}
|
| +
|
| + protected:
|
| +
|
| + private:
|
| + base::MessageLoop message_loop_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(WatchTest);
|
| +};
|
| +
|
| +TEST_F(WatchTest, NotifyBasic) {
|
| + MojoHandle a, b;
|
| + CreateMessagePipe(&a, &b);
|
| +
|
| + base::RunLoop loop;
|
| + WatchHelper b_watcher;
|
| + b_watcher.Watch(
|
| + b, MOJO_HANDLE_SIGNAL_READABLE,
|
| + [&] (MojoResult result, MojoHandleSignalsState state) {
|
| + EXPECT_EQ(MOJO_RESULT_OK, result);
|
| + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
|
| + state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
|
| + EXPECT_TRUE(b_watcher.is_watching());
|
| + loop.Quit();
|
| + });
|
| +
|
| + WriteMessage(a, "Hello!");
|
| + loop.Run();
|
| +
|
| + EXPECT_TRUE(b_watcher.is_watching());
|
| + b_watcher.Cancel();
|
| +
|
| + CloseHandle(a);
|
| + CloseHandle(b);
|
| +}
|
| +
|
| +TEST_F(WatchTest, NotifyUnsatisfiable) {
|
| + MojoHandle a, b;
|
| + CreateMessagePipe(&a, &b);
|
| +
|
| + base::RunLoop loop;
|
| + WatchHelper b_watcher;
|
| + b_watcher.Watch(
|
| + b, MOJO_HANDLE_SIGNAL_READABLE,
|
| + [&] (MojoResult result, MojoHandleSignalsState state) {
|
| + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
|
| + EXPECT_EQ(0u,
|
| + state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
|
| + EXPECT_EQ(0u,
|
| + state.satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE);
|
| + EXPECT_TRUE(b_watcher.is_watching());
|
| + loop.Quit();
|
| + });
|
| +
|
| + CloseHandle(a);
|
| + loop.Run();
|
| +
|
| + b_watcher.Cancel();
|
| +
|
| + CloseHandle(b);
|
| +}
|
| +
|
| +TEST_F(WatchTest, NotifyCancellation) {
|
| + MojoHandle a, b;
|
| + CreateMessagePipe(&a, &b);
|
| +
|
| + base::RunLoop loop;
|
| + WatchHelper b_watcher;
|
| + b_watcher.Watch(
|
| + b, MOJO_HANDLE_SIGNAL_READABLE,
|
| + [&] (MojoResult result, MojoHandleSignalsState state) {
|
| + EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
|
| + EXPECT_EQ(0u, state.satisfied_signals);
|
| + EXPECT_EQ(0u, state.satisfiable_signals);
|
| + EXPECT_FALSE(b_watcher.is_watching());
|
| + loop.Quit();
|
| + });
|
| +
|
| + CloseHandle(b);
|
| + loop.Run();
|
| +
|
| + CloseHandle(a);
|
| +}
|
| +
|
| +TEST_F(WatchTest, InvalidArguemnts) {
|
| + MojoHandle a, b;
|
| + CreateMessagePipe(&a, &b);
|
| +
|
| + uintptr_t context = reinterpret_cast<uintptr_t>(this);
|
| + EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(a, MOJO_HANDLE_SIGNAL_READABLE,
|
| + &IgnoreResult, context));
|
| +
|
| + // Can't cancel a watch that doesn't exist.
|
| + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoCancelWatch(a, ~context));
|
| + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoCancelWatch(b, context));
|
| +
|
| + CloseHandle(a);
|
| + CloseHandle(b);
|
| +
|
| + // Can't watch a handle that doesn't exist.
|
| + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
|
| + MojoWatch(a, MOJO_HANDLE_SIGNAL_READABLE, &IgnoreResult, context));
|
| + EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
|
| + MojoWatch(b, MOJO_HANDLE_SIGNAL_READABLE, &IgnoreResult, context));
|
| +}
|
| +
|
| +TEST_F(WatchTest, NoDuplicateContext) {
|
| + MojoHandle a, b;
|
| + CreateMessagePipe(&a, &b);
|
| +
|
| + // Try to add the same watch twice; should fail.
|
| + uintptr_t context = reinterpret_cast<uintptr_t>(this);
|
| + EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(a, MOJO_HANDLE_SIGNAL_READABLE,
|
| + &IgnoreResult, context));
|
| + EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS,
|
| + MojoWatch(a, MOJO_HANDLE_SIGNAL_READABLE, &IgnoreResult, context));
|
| +
|
| + // Cancel and add it again; should be OK.
|
| + EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(a, context));
|
| + EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(a, MOJO_HANDLE_SIGNAL_READABLE,
|
| + &IgnoreResult, context));
|
| +
|
| + CloseHandle(a);
|
| + CloseHandle(b);
|
| +}
|
| +
|
| +TEST_F(WatchTest, MultipleWatches) {
|
| + MojoHandle a, b;
|
| + CreateMessagePipe(&a, &b);
|
| +
|
| + // Add multiple watchers to |b| and see that they are both notified by a
|
| + // single write to |a|.
|
| + base::RunLoop loop;
|
| + int expected_notifications = 2;
|
| + auto on_readable = [&] (MojoResult result, MojoHandleSignalsState state) {
|
| + EXPECT_EQ(MOJO_RESULT_OK, result);
|
| + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
|
| + state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
|
| + EXPECT_GT(expected_notifications, 0);
|
| + if (--expected_notifications == 0)
|
| + loop.Quit();
|
| + };
|
| + WatchHelper watcher1;
|
| + WatchHelper watcher2;
|
| + watcher1.Watch(b, MOJO_HANDLE_SIGNAL_READABLE, on_readable);
|
| + watcher2.Watch(b, MOJO_HANDLE_SIGNAL_READABLE, on_readable);
|
| +
|
| + WriteMessage(a, "Ping!");
|
| + loop.Run();
|
| +
|
| + watcher1.Cancel();
|
| + watcher2.Cancel();
|
| +
|
| + CloseHandle(a);
|
| + CloseHandle(b);
|
| +}
|
| +
|
| +TEST_F(WatchTest, WatchWhileSatisfied) {
|
| + MojoHandle a, b;
|
| + CreateMessagePipe(&a, &b);
|
| +
|
| + // Write to |a| and then start watching |b|. The callback should be invoked
|
| + // synchronously.
|
| + WriteMessage(a, "hey");
|
| + bool signaled = false;
|
| + WatchHelper b_watcher;
|
| + b_watcher.Watch(
|
| + b, MOJO_HANDLE_SIGNAL_READABLE,
|
| + [&] (MojoResult result, MojoHandleSignalsState state) {
|
| + EXPECT_EQ(MOJO_RESULT_OK, result);
|
| + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
|
| + state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
|
| + signaled = true;
|
| + });
|
| + EXPECT_TRUE(signaled);
|
| + b_watcher.Cancel();
|
| +
|
| + CloseHandle(a);
|
| + CloseHandle(b);
|
| +}
|
| +
|
| +TEST_F(WatchTest, WatchWhileUnsatisfiable) {
|
| + MojoHandle a, b;
|
| + CreateMessagePipe(&a, &b);
|
| +
|
| + // Close |a| and then try to watch |b|. MojoWatch() should fail.
|
| + CloseHandle(a);
|
| + uintptr_t context = reinterpret_cast<uintptr_t>(this);
|
| + EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
|
| + MojoWatch(b, MOJO_HANDLE_SIGNAL_READABLE, &IgnoreResult, context));
|
| +
|
| + CloseHandle(b);
|
| +}
|
| +
|
| +TEST_F(WatchTest, RespondFromCallback) {
|
| + MojoHandle a, b;
|
| + CreateMessagePipe(&a, &b);
|
| +
|
| + // Watch |a| and |b|. Write to |a|, then write to |b| from within the callback
|
| + // which notifies it of the available message.
|
| + const std::string kTestMessage = "hello worlds.";
|
| + base::RunLoop loop;
|
| + WatchHelper b_watcher;
|
| + b_watcher.Watch(
|
| + b, MOJO_HANDLE_SIGNAL_READABLE,
|
| + [&] (MojoResult result, MojoHandleSignalsState state) {
|
| + EXPECT_EQ(MOJO_RESULT_OK, result);
|
| + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
|
| + state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
|
| + EXPECT_TRUE(b_watcher.is_watching());
|
| +
|
| + // Echo a's message back to it.
|
| + WriteMessage(b, ReadMessage(b));
|
| + });
|
| +
|
| + WatchHelper a_watcher;
|
| + a_watcher.Watch(
|
| + a, MOJO_HANDLE_SIGNAL_READABLE,
|
| + [&] (MojoResult result, MojoHandleSignalsState state) {
|
| + EXPECT_EQ(MOJO_RESULT_OK, result);
|
| + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
|
| + state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
|
| + EXPECT_TRUE(a_watcher.is_watching());
|
| +
|
| + // Expect to receive back the message that was originally sent to |b|.
|
| + EXPECT_EQ(kTestMessage, ReadMessage(a));
|
| +
|
| + loop.Quit();
|
| + });
|
| +
|
| + WriteMessage(a, kTestMessage);
|
| + loop.Run();
|
| +
|
| + a_watcher.Cancel();
|
| + b_watcher.Cancel();
|
| +
|
| + CloseHandle(a);
|
| + CloseHandle(b);
|
| +}
|
| +
|
| +TEST_F(WatchTest, WatchDataPipeConsumer) {
|
| + MojoHandle a, b;
|
| + CreateDataPipe(&a, &b, 64);
|
| +
|
| + base::RunLoop loop;
|
| + WatchHelper b_watcher;
|
| + b_watcher.Watch(
|
| + b, MOJO_HANDLE_SIGNAL_READABLE,
|
| + [&] (MojoResult result, MojoHandleSignalsState state) {
|
| + EXPECT_EQ(MOJO_RESULT_OK, result);
|
| + EXPECT_EQ(MOJO_HANDLE_SIGNAL_READABLE,
|
| + state.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE);
|
| + EXPECT_TRUE(b_watcher.is_watching());
|
| + loop.Quit();
|
| + });
|
| +
|
| + WriteData(a, "Hello!");
|
| + loop.Run();
|
| +
|
| + EXPECT_TRUE(b_watcher.is_watching());
|
| + b_watcher.Cancel();
|
| +
|
| + CloseHandle(a);
|
| + CloseHandle(b);
|
| +}
|
| +
|
| +TEST_F(WatchTest, WatchDataPipeProducer) {
|
| + MojoHandle a, b;
|
| + CreateDataPipe(&a, &b, 8);
|
| +
|
| + // Fill the pipe to capacity so writes will block.
|
| + WriteData(a, "xxxxxxxx");
|
| +
|
| + base::RunLoop loop;
|
| + WatchHelper a_watcher;
|
| + a_watcher.Watch(
|
| + a, MOJO_HANDLE_SIGNAL_WRITABLE,
|
| + [&] (MojoResult result, MojoHandleSignalsState state) {
|
| + EXPECT_EQ(MOJO_RESULT_OK, result);
|
| + EXPECT_EQ(MOJO_HANDLE_SIGNAL_WRITABLE,
|
| + state.satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE);
|
| + EXPECT_TRUE(a_watcher.is_watching());
|
| + loop.Quit();
|
| + });
|
| +
|
| + EXPECT_EQ("xxxxxxxx", ReadData(b, 8));
|
| + loop.Run();
|
| +
|
| + EXPECT_TRUE(a_watcher.is_watching());
|
| + a_watcher.Cancel();
|
| +
|
| + CloseHandle(a);
|
| + CloseHandle(b);
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace edk
|
| +} // namespace mojo
|
|
|