123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- // 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/containers/span.h"
- #include "base/files/file_util.h"
- #include "base/memory/ptr_util.h"
- #include "base/message_loop/message_pump_type.h"
- #include "base/posix/eintr_wrapper.h"
- #include "base/run_loop.h"
- #include "base/test/task_environment.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 "base/threading/thread_task_runner_handle.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(const Mock&) = delete;
- Mock& operator=(const Mock&) = delete;
- MOCK_METHOD0(ReadableCallback, void());
- MOCK_METHOD0(WritableCallback, void());
- };
- enum class FileDescriptorWatcherTestType {
- MESSAGE_PUMP_FOR_IO_ON_MAIN_THREAD,
- MESSAGE_PUMP_FOR_IO_ON_OTHER_THREAD,
- };
- class FileDescriptorWatcherTest
- : public testing::TestWithParam<FileDescriptorWatcherTestType> {
- public:
- FileDescriptorWatcherTest()
- : task_environment_(std::make_unique<test::TaskEnvironment>(
- GetParam() == FileDescriptorWatcherTestType::
- MESSAGE_PUMP_FOR_IO_ON_MAIN_THREAD
- ? test::TaskEnvironment::MainThreadType::IO
- : test::TaskEnvironment::MainThreadType::DEFAULT)),
- other_thread_("FileDescriptorWatcherTest_OtherThread") {}
- FileDescriptorWatcherTest(const FileDescriptorWatcherTest&) = delete;
- FileDescriptorWatcherTest& operator=(const FileDescriptorWatcherTest&) =
- delete;
- ~FileDescriptorWatcherTest() override = default;
- void SetUp() override {
- ASSERT_EQ(0, pipe(pipe_fds_));
- scoped_refptr<SingleThreadTaskRunner> io_thread_task_runner;
- if (GetParam() ==
- FileDescriptorWatcherTestType::MESSAGE_PUMP_FOR_IO_ON_OTHER_THREAD) {
- Thread::Options options;
- options.message_pump_type = MessagePumpType::IO;
- ASSERT_TRUE(other_thread_.StartWithOptions(std::move(options)));
- file_descriptor_watcher_ =
- std::make_unique<FileDescriptorWatcher>(other_thread_.task_runner());
- }
- }
- void TearDown() override {
- if (GetParam() ==
- FileDescriptorWatcherTestType::MESSAGE_PUMP_FOR_IO_ON_MAIN_THREAD &&
- task_environment_) {
- // 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(),
- BindRepeating(&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(),
- BindRepeating(&Mock::WritableCallback, Unretained(&mock_)));
- EXPECT_TRUE(controller);
- return controller;
- }
- void WriteByte() {
- constexpr char kByte = '!';
- ASSERT_TRUE(WriteFileDescriptor(write_file_descriptor(),
- as_bytes(make_span(&kByte, 1))));
- }
- 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_;
- // Task environment bound to the main thread.
- std::unique_ptr<test::TaskEnvironment> task_environment_;
- // Thread running an IO message pump. Used when the test type is
- // MESSAGE_PUMP_FOR_IO_ON_OTHER_THREAD.
- Thread other_thread_;
- private:
- // Used to listen for file descriptor events on |other_thread_|. The scoped
- // task environment implements a watcher for the main thread case.
- 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_;
- };
- } // 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, DeleteControllerAfterDeleteMessagePumpForIO) {
- auto controller = WatchReadable();
- // Delete the task environment.
- if (GetParam() ==
- FileDescriptorWatcherTestType::MESSAGE_PUMP_FOR_IO_ON_MAIN_THREAD) {
- task_environment_.reset();
- } else {
- other_thread_.Stop();
- }
- // Deleting |controller| shouldn't crash even though that causes a task to be
- // posted to the message pump thread.
- controller = nullptr;
- }
- INSTANTIATE_TEST_SUITE_P(
- MessagePumpForIOOnMainThread,
- FileDescriptorWatcherTest,
- ::testing::Values(
- FileDescriptorWatcherTestType::MESSAGE_PUMP_FOR_IO_ON_MAIN_THREAD));
- INSTANTIATE_TEST_SUITE_P(
- MessagePumpForIOOnOtherThread,
- FileDescriptorWatcherTest,
- ::testing::Values(
- FileDescriptorWatcherTestType::MESSAGE_PUMP_FOR_IO_ON_OTHER_THREAD));
- } // namespace base
|