| 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..5a92b70af2522040215a2c1583ac163be955d11f
|
| --- /dev/null
|
| +++ b/mojo/edk/system/watch_unittest.cc
|
| @@ -0,0 +1,329 @@
|
| +// 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) {
|
| +}
|
| +
|
| +class Watcher {
|
| + public:
|
| + using Callback =
|
| + std::function<void(MojoResult result, MojoHandleSignalsState state)>;
|
| +
|
| + Watcher(MojoHandle handle,
|
| + MojoHandleSignals signals,
|
| + const Callback& callback)
|
| + : handle_(handle), callback_(callback) {
|
| + watching_ = true;
|
| + CHECK_EQ(MOJO_RESULT_OK, MojoWatch(handle_, signals, &Watcher::OnNotify,
|
| + reinterpret_cast<uintptr_t>(this)));
|
| + }
|
| +
|
| + ~Watcher() {
|
| + if (watching_)
|
| + Cancel();
|
| + }
|
| +
|
| + bool is_watching() const { return watching_; }
|
| +
|
| + void Cancel() {
|
| + CHECK(watching_);
|
| + watching_ = false;
|
| + CHECK_EQ(MOJO_RESULT_OK,
|
| + MojoCancelWatch(handle_, reinterpret_cast<uintptr_t>(this)));
|
| + }
|
| +
|
| + private:
|
| + static void OnNotify(uintptr_t context,
|
| + MojoResult result,
|
| + MojoHandleSignalsState state) {
|
| + Watcher* watcher = reinterpret_cast<Watcher*>(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(Watcher);
|
| +};
|
| +
|
| +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;
|
| + Watcher b_watcher(
|
| + 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;
|
| + Watcher b_watcher(
|
| + 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;
|
| + Watcher b_watcher(
|
| + 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, MultipleWatchers) {
|
| + 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();
|
| + };
|
| + Watcher watcher1(b, MOJO_HANDLE_SIGNAL_READABLE, on_readable);
|
| + Watcher watcher2(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;
|
| + Watcher b_watcher(
|
| + 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, CancelFromCallback) {
|
| + MojoHandle a, b;
|
| + CreateMessagePipe(&a, &b);
|
| +
|
| + // Verify that we can safely cancel the watcher from within its own callback.
|
| + base::RunLoop loop;
|
| + Watcher b_watcher(
|
| + 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());
|
| + b_watcher.Cancel();
|
| + loop.Quit();
|
| + });
|
| + WriteMessage(a, "hey!");
|
| + loop.Run();
|
| +
|
| + 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. Then cancel both watchers from
|
| + // |a|'s callback before quitting.
|
| + const std::string kTestMessage = "hello worlds.";
|
| + base::RunLoop loop;
|
| + Watcher b_watcher(
|
| + 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));
|
| + });
|
| +
|
| + Watcher a_watcher(
|
| + 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));
|
| +
|
| + a_watcher.Cancel();
|
| + b_watcher.Cancel();
|
| +
|
| + loop.Quit();
|
| + });
|
| +
|
| + WriteMessage(a, kTestMessage);
|
| + loop.Run();
|
| +
|
| + CloseHandle(a);
|
| + CloseHandle(b);
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace edk
|
| +} // namespace mojo
|
|
|