diff options
Diffstat (limited to 'mojo/edk/system/watch_unittest.cc')
-rw-r--r-- | mojo/edk/system/watch_unittest.cc | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/mojo/edk/system/watch_unittest.cc b/mojo/edk/system/watch_unittest.cc new file mode 100644 index 0000000..fd0c13d --- /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 |