|  | // 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 "base/files/file_descriptor_watcher_posix.h" | 
|  |  | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/files/file_util.h" | 
|  | #include "base/macros.h" | 
|  | #include "base/memory/ptr_util.h" | 
|  | #include "base/message_loop/message_loop.h" | 
|  | #include "base/posix/eintr_wrapper.h" | 
|  | #include "base/run_loop.h" | 
|  | #include "base/test/test_timeouts.h" | 
|  | #include "base/threading/platform_thread.h" | 
|  | #include "base/threading/thread.h" | 
|  | #include "base/threading/thread_checker_impl.h" | 
|  | #include "build/build_config.h" | 
|  | #include "testing/gmock/include/gmock/gmock.h" | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  |  | 
|  | namespace base { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | class Mock { | 
|  | public: | 
|  | Mock() = default; | 
|  |  | 
|  | MOCK_METHOD0(ReadableCallback, void()); | 
|  | MOCK_METHOD0(WritableCallback, void()); | 
|  |  | 
|  | private: | 
|  | DISALLOW_COPY_AND_ASSIGN(Mock); | 
|  | }; | 
|  |  | 
|  | enum class FileDescriptorWatcherTestType { | 
|  | MESSAGE_LOOP_FOR_IO_ON_MAIN_THREAD, | 
|  | MESSAGE_LOOP_FOR_IO_ON_OTHER_THREAD, | 
|  | }; | 
|  |  | 
|  | class FileDescriptorWatcherTest | 
|  | : public testing::TestWithParam<FileDescriptorWatcherTestType> { | 
|  | public: | 
|  | FileDescriptorWatcherTest() | 
|  | : message_loop_(GetParam() == FileDescriptorWatcherTestType:: | 
|  | MESSAGE_LOOP_FOR_IO_ON_MAIN_THREAD | 
|  | ? new MessageLoopForIO | 
|  | : new MessageLoop), | 
|  | other_thread_("FileDescriptorWatcherTest_OtherThread") {} | 
|  | ~FileDescriptorWatcherTest() override = default; | 
|  |  | 
|  | void SetUp() override { | 
|  | ASSERT_EQ(0, pipe(pipe_fds_)); | 
|  |  | 
|  | MessageLoop* message_loop_for_io; | 
|  | if (GetParam() == | 
|  | FileDescriptorWatcherTestType::MESSAGE_LOOP_FOR_IO_ON_OTHER_THREAD) { | 
|  | Thread::Options options; | 
|  | options.message_loop_type = MessageLoop::TYPE_IO; | 
|  | ASSERT_TRUE(other_thread_.StartWithOptions(options)); | 
|  | message_loop_for_io = other_thread_.message_loop(); | 
|  | } else { | 
|  | message_loop_for_io = message_loop_.get(); | 
|  | } | 
|  |  | 
|  | ASSERT_TRUE(message_loop_for_io->IsType(MessageLoop::TYPE_IO)); | 
|  | file_descriptor_watcher_ = std::make_unique<FileDescriptorWatcher>( | 
|  | static_cast<MessageLoopForIO*>(message_loop_for_io)); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | if (GetParam() == | 
|  | FileDescriptorWatcherTestType::MESSAGE_LOOP_FOR_IO_ON_MAIN_THREAD && | 
|  | message_loop_) { | 
|  | // Allow the delete task posted by the Controller's destructor to run. | 
|  | base::RunLoop().RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | // Ensure that OtherThread is done processing before closing fds. | 
|  | other_thread_.Stop(); | 
|  |  | 
|  | EXPECT_EQ(0, IGNORE_EINTR(close(pipe_fds_[0]))); | 
|  | EXPECT_EQ(0, IGNORE_EINTR(close(pipe_fds_[1]))); | 
|  | } | 
|  |  | 
|  | protected: | 
|  | int read_file_descriptor() const { return pipe_fds_[0]; } | 
|  | int write_file_descriptor() const { return pipe_fds_[1]; } | 
|  |  | 
|  | // Waits for a short delay and run pending tasks. | 
|  | void WaitAndRunPendingTasks() { | 
|  | PlatformThread::Sleep(TestTimeouts::tiny_timeout()); | 
|  | RunLoop().RunUntilIdle(); | 
|  | } | 
|  |  | 
|  | // Registers ReadableCallback() to be called on |mock_| when | 
|  | // read_file_descriptor() is readable without blocking. | 
|  | std::unique_ptr<FileDescriptorWatcher::Controller> WatchReadable() { | 
|  | std::unique_ptr<FileDescriptorWatcher::Controller> controller = | 
|  | FileDescriptorWatcher::WatchReadable( | 
|  | read_file_descriptor(), | 
|  | Bind(&Mock::ReadableCallback, Unretained(&mock_))); | 
|  | EXPECT_TRUE(controller); | 
|  |  | 
|  | // Unless read_file_descriptor() was readable before the callback was | 
|  | // registered, this shouldn't do anything. | 
|  | WaitAndRunPendingTasks(); | 
|  |  | 
|  | return controller; | 
|  | } | 
|  |  | 
|  | // Registers WritableCallback() to be called on |mock_| when | 
|  | // write_file_descriptor() is writable without blocking. | 
|  | std::unique_ptr<FileDescriptorWatcher::Controller> WatchWritable() { | 
|  | std::unique_ptr<FileDescriptorWatcher::Controller> controller = | 
|  | FileDescriptorWatcher::WatchWritable( | 
|  | write_file_descriptor(), | 
|  | Bind(&Mock::WritableCallback, Unretained(&mock_))); | 
|  | EXPECT_TRUE(controller); | 
|  | return controller; | 
|  | } | 
|  |  | 
|  | void WriteByte() { | 
|  | constexpr char kByte = '!'; | 
|  | ASSERT_TRUE( | 
|  | WriteFileDescriptor(write_file_descriptor(), &kByte, sizeof(kByte))); | 
|  | } | 
|  |  | 
|  | void ReadByte() { | 
|  | // This is always called as part of the WatchReadable() callback, which | 
|  | // should run on the main thread. | 
|  | EXPECT_TRUE(thread_checker_.CalledOnValidThread()); | 
|  |  | 
|  | char buffer; | 
|  | ASSERT_TRUE(ReadFromFD(read_file_descriptor(), &buffer, sizeof(buffer))); | 
|  | } | 
|  |  | 
|  | // Mock on wich callbacks are invoked. | 
|  | testing::StrictMock<Mock> mock_; | 
|  |  | 
|  | // MessageLoop bound to the main thread. | 
|  | std::unique_ptr<MessageLoop> message_loop_; | 
|  |  | 
|  | // Thread running a MessageLoopForIO. Used when the test type is | 
|  | // MESSAGE_LOOP_FOR_IO_ON_OTHER_THREAD. | 
|  | Thread other_thread_; | 
|  |  | 
|  | private: | 
|  | // Determines which MessageLoopForIO is used to watch file descriptors for | 
|  | // which callbacks are registered on the main thread. | 
|  | std::unique_ptr<FileDescriptorWatcher> file_descriptor_watcher_; | 
|  |  | 
|  | // Watched file descriptors. | 
|  | int pipe_fds_[2]; | 
|  |  | 
|  | // Used to verify that callbacks run on the thread on which they are | 
|  | // registered. | 
|  | ThreadCheckerImpl thread_checker_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcherTest); | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | TEST_P(FileDescriptorWatcherTest, WatchWritable) { | 
|  | auto controller = WatchWritable(); | 
|  |  | 
|  | // The write end of a newly created pipe is immediately writable. | 
|  | RunLoop run_loop; | 
|  | EXPECT_CALL(mock_, WritableCallback()) | 
|  | .WillOnce(testing::Invoke(&run_loop, &RunLoop::Quit)); | 
|  | run_loop.Run(); | 
|  | } | 
|  |  | 
|  | TEST_P(FileDescriptorWatcherTest, WatchReadableOneByte) { | 
|  | auto controller = WatchReadable(); | 
|  |  | 
|  | // Write 1 byte to the pipe, making it readable without blocking. Expect one | 
|  | // call to ReadableCallback() which will read 1 byte from the pipe. | 
|  | WriteByte(); | 
|  | RunLoop run_loop; | 
|  | EXPECT_CALL(mock_, ReadableCallback()) | 
|  | .WillOnce(testing::Invoke([this, &run_loop]() { | 
|  | ReadByte(); | 
|  | run_loop.Quit(); | 
|  | })); | 
|  | run_loop.Run(); | 
|  | testing::Mock::VerifyAndClear(&mock_); | 
|  |  | 
|  | // No more call to ReadableCallback() is expected. | 
|  | WaitAndRunPendingTasks(); | 
|  | } | 
|  |  | 
|  | TEST_P(FileDescriptorWatcherTest, WatchReadableTwoBytes) { | 
|  | auto controller = WatchReadable(); | 
|  |  | 
|  | // Write 2 bytes to the pipe. Expect two calls to ReadableCallback() which | 
|  | // will each read 1 byte from the pipe. | 
|  | WriteByte(); | 
|  | WriteByte(); | 
|  | RunLoop run_loop; | 
|  | EXPECT_CALL(mock_, ReadableCallback()) | 
|  | .WillOnce(testing::Invoke([this]() { ReadByte(); })) | 
|  | .WillOnce(testing::Invoke([this, &run_loop]() { | 
|  | ReadByte(); | 
|  | run_loop.Quit(); | 
|  | })); | 
|  | run_loop.Run(); | 
|  | testing::Mock::VerifyAndClear(&mock_); | 
|  |  | 
|  | // No more call to ReadableCallback() is expected. | 
|  | WaitAndRunPendingTasks(); | 
|  | } | 
|  |  | 
|  | TEST_P(FileDescriptorWatcherTest, WatchReadableByteWrittenFromCallback) { | 
|  | auto controller = WatchReadable(); | 
|  |  | 
|  | // Write 1 byte to the pipe. Expect one call to ReadableCallback() from which | 
|  | // 1 byte is read and 1 byte is written to the pipe. Then, expect another call | 
|  | // to ReadableCallback() from which the remaining byte is read from the pipe. | 
|  | WriteByte(); | 
|  | RunLoop run_loop; | 
|  | EXPECT_CALL(mock_, ReadableCallback()) | 
|  | .WillOnce(testing::Invoke([this]() { | 
|  | ReadByte(); | 
|  | WriteByte(); | 
|  | })) | 
|  | .WillOnce(testing::Invoke([this, &run_loop]() { | 
|  | ReadByte(); | 
|  | run_loop.Quit(); | 
|  | })); | 
|  | run_loop.Run(); | 
|  | testing::Mock::VerifyAndClear(&mock_); | 
|  |  | 
|  | // No more call to ReadableCallback() is expected. | 
|  | WaitAndRunPendingTasks(); | 
|  | } | 
|  |  | 
|  | TEST_P(FileDescriptorWatcherTest, DeleteControllerFromCallback) { | 
|  | auto controller = WatchReadable(); | 
|  |  | 
|  | // Write 1 byte to the pipe. Expect one call to ReadableCallback() from which | 
|  | // |controller| is deleted. | 
|  | WriteByte(); | 
|  | RunLoop run_loop; | 
|  | EXPECT_CALL(mock_, ReadableCallback()) | 
|  | .WillOnce(testing::Invoke([&run_loop, &controller]() { | 
|  | controller = nullptr; | 
|  | run_loop.Quit(); | 
|  | })); | 
|  | run_loop.Run(); | 
|  | testing::Mock::VerifyAndClear(&mock_); | 
|  |  | 
|  | // Since |controller| has been deleted, no call to ReadableCallback() is | 
|  | // expected even though the pipe is still readable without blocking. | 
|  | WaitAndRunPendingTasks(); | 
|  | } | 
|  |  | 
|  | TEST_P(FileDescriptorWatcherTest, | 
|  | DeleteControllerBeforeFileDescriptorReadable) { | 
|  | auto controller = WatchReadable(); | 
|  |  | 
|  | // Cancel the watch. | 
|  | controller = nullptr; | 
|  |  | 
|  | // Write 1 byte to the pipe to make it readable without blocking. | 
|  | WriteByte(); | 
|  |  | 
|  | // No call to ReadableCallback() is expected. | 
|  | WaitAndRunPendingTasks(); | 
|  | } | 
|  |  | 
|  | TEST_P(FileDescriptorWatcherTest, DeleteControllerAfterFileDescriptorReadable) { | 
|  | auto controller = WatchReadable(); | 
|  |  | 
|  | // Write 1 byte to the pipe to make it readable without blocking. | 
|  | WriteByte(); | 
|  |  | 
|  | // Cancel the watch. | 
|  | controller = nullptr; | 
|  |  | 
|  | // No call to ReadableCallback() is expected. | 
|  | WaitAndRunPendingTasks(); | 
|  | } | 
|  |  | 
|  | TEST_P(FileDescriptorWatcherTest, DeleteControllerAfterDeleteMessageLoopForIO) { | 
|  | auto controller = WatchReadable(); | 
|  |  | 
|  | // Delete the MessageLoopForIO. | 
|  | if (GetParam() == | 
|  | FileDescriptorWatcherTestType::MESSAGE_LOOP_FOR_IO_ON_MAIN_THREAD) { | 
|  | message_loop_ = nullptr; | 
|  | } else { | 
|  | other_thread_.Stop(); | 
|  | } | 
|  |  | 
|  | // Deleting |controller| shouldn't crash even though that causes a task to be | 
|  | // posted to the MessageLoopForIO thread. | 
|  | controller = nullptr; | 
|  | } | 
|  |  | 
|  | INSTANTIATE_TEST_CASE_P( | 
|  | MessageLoopForIOOnMainThread, | 
|  | FileDescriptorWatcherTest, | 
|  | ::testing::Values( | 
|  | FileDescriptorWatcherTestType::MESSAGE_LOOP_FOR_IO_ON_MAIN_THREAD)); | 
|  | INSTANTIATE_TEST_CASE_P( | 
|  | MessageLoopForIOOnOtherThread, | 
|  | FileDescriptorWatcherTest, | 
|  | ::testing::Values( | 
|  | FileDescriptorWatcherTestType::MESSAGE_LOOP_FOR_IO_ON_OTHER_THREAD)); | 
|  |  | 
|  | }  // namespace base |