Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1564)

Unified Diff: base/files/file_descriptor_watcher_posix_unittest.cc

Issue 2332923004: Add a new file descriptor watch API. (Closed)
Patch Set: Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: base/files/file_descriptor_watcher_posix_unittest.cc
diff --git a/base/files/file_descriptor_watcher_posix_unittest.cc b/base/files/file_descriptor_watcher_posix_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..5171747bd7eb0feec6ccaa9dd4e71c0fc5a2aa00
--- /dev/null
+++ b/base/files/file_descriptor_watcher_posix_unittest.cc
@@ -0,0 +1,318 @@
+// 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_ = MakeUnique<FileDescriptorWatcher>(
+ reinterpret_cast<MessageLoopForIO*>(message_loop_for_io));
dcheng 2016/09/14 06:48:19 static_cast should work and look slightly less sca
fdoray 2016/09/14 17:06:14 Done.
+ }
+
+ 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();
+ }
+
+ 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(
+ read_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();
+
+// On Mac and iOS, the write end of a newly created pipe is writable without
+// blocking.
+#if defined(OS_MACOSX)
+ RunLoop run_loop;
+ EXPECT_CALL(mock_, WritableCallback())
+ .WillOnce(testing::Invoke(&run_loop, &RunLoop::Quit));
+ run_loop.Run();
+#endif // defined(OS_MACOSX)
+}
+
+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
« base/files/file_descriptor_watcher_posix.cc ('K') | « base/files/file_descriptor_watcher_posix.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698