// 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 "mojo/public/cpp/system/simple_watcher.h" #include #include "base/bind.h" #include "base/callback.h" #include "base/macros.h" #include "base/message_loop/message_loop.h" #include "base/run_loop.h" #include "base/threading/thread_task_runner_handle.h" #include "mojo/public/c/system/types.h" #include "mojo/public/cpp/system/data_pipe.h" #include "mojo/public/cpp/system/message_pipe.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { namespace { template void RunResultHandler(Handler f, MojoResult result) { f(result); } template SimpleWatcher::ReadyCallback OnReady(Handler f) { return base::Bind(&RunResultHandler, f); } SimpleWatcher::ReadyCallback NotReached() { return OnReady([](MojoResult) { NOTREACHED(); }); } class SimpleWatcherTest : public testing::Test { public: SimpleWatcherTest() {} ~SimpleWatcherTest() override {} private: base::MessageLoop message_loop_; DISALLOW_COPY_AND_ASSIGN(SimpleWatcherTest); }; TEST_F(SimpleWatcherTest, WatchBasic) { ScopedMessagePipeHandle a, b; CreateMessagePipe(nullptr, &a, &b); bool notified = false; base::RunLoop run_loop; SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC, base::SequencedTaskRunnerHandle::Get()); EXPECT_EQ(MOJO_RESULT_OK, b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, OnReady([&](MojoResult result) { EXPECT_EQ(MOJO_RESULT_OK, result); notified = true; run_loop.Quit(); }))); EXPECT_TRUE(b_watcher.IsWatching()); EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); run_loop.Run(); EXPECT_TRUE(notified); b_watcher.Cancel(); } TEST_F(SimpleWatcherTest, WatchUnsatisfiable) { ScopedMessagePipeHandle a, b; CreateMessagePipe(nullptr, &a, &b); a.reset(); SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL, base::SequencedTaskRunnerHandle::Get()); EXPECT_EQ( MOJO_RESULT_OK, b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, NotReached())); EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, b_watcher.Arm()); } TEST_F(SimpleWatcherTest, WatchFailedPreconditionNoSpam) { DataPipe pipe; bool had_failed_precondition = false; SimpleWatcher watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC); MojoResult rc = watcher.Watch(pipe.consumer_handle.get(), MOJO_HANDLE_SIGNAL_READABLE, OnReady([&](MojoResult result) { EXPECT_FALSE(had_failed_precondition); switch (result) { case MOJO_RESULT_OK: const void* begin; uint32_t num_bytes; pipe.consumer_handle->BeginReadData( &begin, &num_bytes, MOJO_READ_DATA_FLAG_NONE); pipe.consumer_handle->EndReadData(num_bytes); break; case MOJO_RESULT_FAILED_PRECONDITION: had_failed_precondition = true; break; } })); EXPECT_EQ(MOJO_RESULT_OK, rc); uint32_t size = 5; EXPECT_EQ(MOJO_RESULT_OK, pipe.producer_handle->WriteData( "hello", &size, MOJO_WRITE_DATA_FLAG_NONE)); base::RunLoop().RunUntilIdle(); pipe.producer_handle.reset(); base::RunLoop().RunUntilIdle(); EXPECT_TRUE(had_failed_precondition); } TEST_F(SimpleWatcherTest, WatchInvalidHandle) { ScopedMessagePipeHandle a, b; CreateMessagePipe(nullptr, &a, &b); a.reset(); b.reset(); SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC, base::SequencedTaskRunnerHandle::Get()); EXPECT_EQ( MOJO_RESULT_INVALID_ARGUMENT, b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, NotReached())); EXPECT_FALSE(b_watcher.IsWatching()); } TEST_F(SimpleWatcherTest, Cancel) { ScopedMessagePipeHandle a, b; CreateMessagePipe(nullptr, &a, &b); base::RunLoop run_loop; SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC, base::SequencedTaskRunnerHandle::Get()); EXPECT_EQ( MOJO_RESULT_OK, b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, NotReached())); EXPECT_TRUE(b_watcher.IsWatching()); b_watcher.Cancel(); EXPECT_FALSE(b_watcher.IsWatching()); // This should never trigger the watcher. EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop.QuitClosure()); run_loop.Run(); } TEST_F(SimpleWatcherTest, CancelOnClose) { ScopedMessagePipeHandle a, b; CreateMessagePipe(nullptr, &a, &b); base::RunLoop run_loop; SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC, base::SequencedTaskRunnerHandle::Get()); EXPECT_EQ(MOJO_RESULT_OK, b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, OnReady([&](MojoResult result) { EXPECT_EQ(MOJO_RESULT_CANCELLED, result); run_loop.Quit(); }))); EXPECT_TRUE(b_watcher.IsWatching()); // This should trigger the watcher above. b.reset(); run_loop.Run(); EXPECT_FALSE(b_watcher.IsWatching()); } TEST_F(SimpleWatcherTest, CancelOnDestruction) { ScopedMessagePipeHandle a, b; CreateMessagePipe(nullptr, &a, &b); base::RunLoop run_loop; { SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC, base::SequencedTaskRunnerHandle::Get()); EXPECT_EQ( MOJO_RESULT_OK, b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, NotReached())); EXPECT_TRUE(b_watcher.IsWatching()); // |b_watcher| should be cancelled when it goes out of scope. } // This should never trigger the watcher above. EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, run_loop.QuitClosure()); run_loop.Run(); } TEST_F(SimpleWatcherTest, CloseAndCancel) { ScopedMessagePipeHandle a, b; CreateMessagePipe(nullptr, &a, &b); SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::AUTOMATIC, base::SequencedTaskRunnerHandle::Get()); EXPECT_EQ(MOJO_RESULT_OK, b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, OnReady([](MojoResult result) { FAIL(); }))); EXPECT_TRUE(b_watcher.IsWatching()); // This should trigger the watcher above... b.reset(); // ...but the watcher is cancelled first. b_watcher.Cancel(); EXPECT_FALSE(b_watcher.IsWatching()); base::RunLoop().RunUntilIdle(); } TEST_F(SimpleWatcherTest, UnarmedCancel) { ScopedMessagePipeHandle a, b; CreateMessagePipe(nullptr, &a, &b); SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL, base::SequencedTaskRunnerHandle::Get()); base::RunLoop loop; EXPECT_EQ(MOJO_RESULT_OK, b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, base::Bind( [](base::RunLoop* loop, MojoResult result) { EXPECT_EQ(result, MOJO_RESULT_CANCELLED); loop->Quit(); }, &loop))); // This message write will not wake up the watcher since the watcher isn't // armed. Instead, the cancellation will dispatch due to the reset below. EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); b.reset(); loop.Run(); } TEST_F(SimpleWatcherTest, ManualArming) { ScopedMessagePipeHandle a, b; CreateMessagePipe(nullptr, &a, &b); SimpleWatcher b_watcher(FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL, base::SequencedTaskRunnerHandle::Get()); base::RunLoop loop; EXPECT_EQ(MOJO_RESULT_OK, b_watcher.Watch(b.get(), MOJO_HANDLE_SIGNAL_READABLE, base::Bind( [](base::RunLoop* loop, MojoResult result) { EXPECT_EQ(result, MOJO_RESULT_OK); loop->Quit(); }, &loop))); EXPECT_EQ(MOJO_RESULT_OK, b_watcher.Arm()); EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); loop.Run(); } TEST_F(SimpleWatcherTest, ManualArmOrNotifyWhileSignaled) { ScopedMessagePipeHandle a, b; CreateMessagePipe(nullptr, &a, &b); base::RunLoop loop1; SimpleWatcher b_watcher1(FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL); bool notified1 = false; EXPECT_EQ(MOJO_RESULT_OK, b_watcher1.Watch( b.get(), MOJO_HANDLE_SIGNAL_READABLE, base::Bind( [](base::RunLoop* loop, bool* notified, MojoResult result) { EXPECT_EQ(result, MOJO_RESULT_OK); *notified = true; loop->Quit(); }, &loop1, ¬ified1))); base::RunLoop loop2; SimpleWatcher b_watcher2(FROM_HERE, SimpleWatcher::ArmingPolicy::MANUAL); bool notified2 = false; EXPECT_EQ(MOJO_RESULT_OK, b_watcher2.Watch( b.get(), MOJO_HANDLE_SIGNAL_READABLE, base::Bind( [](base::RunLoop* loop, bool* notified, MojoResult result) { EXPECT_EQ(result, MOJO_RESULT_OK); *notified = true; loop->Quit(); }, &loop2, ¬ified2))); // First ensure that |b| is readable. EXPECT_EQ(MOJO_RESULT_OK, b_watcher1.Arm()); EXPECT_EQ(MOJO_RESULT_OK, WriteMessageRaw(a.get(), "hello", 5, nullptr, 0, MOJO_WRITE_MESSAGE_FLAG_NONE)); loop1.Run(); EXPECT_TRUE(notified1); EXPECT_FALSE(notified2); notified1 = false; // Now verify that ArmOrNotify results in a notification. b_watcher2.ArmOrNotify(); loop2.Run(); EXPECT_FALSE(notified1); EXPECT_TRUE(notified2); } } // namespace } // namespace mojo