blob: 96b55eb7bc5fc793d08dd0dcfd75e2e0b8c8b81c [file] [log] [blame]
// Copyright 2017 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/compiler_specific.h"
#include "base/files/file.h"
#include "base/files/file_util.h"
#include "base/logging.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 "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
#if !defined(OS_NACL)
namespace {
class MessageLoopForIoPosixTest : public testing::Test {
public:
MessageLoopForIoPosixTest() {}
// testing::Test interface.
void SetUp() override {
// Create a file descriptor. Doesn't need to be readable or writable,
// as we don't need to actually get any notifications.
// pipe() is just the easiest way to do it.
int pipefds[2];
int err = pipe(pipefds);
ASSERT_EQ(0, err);
read_fd_ = base::File(pipefds[0]);
write_fd_ = base::File(pipefds[1]);
}
base::File read_fd_;
base::File write_fd_;
DISALLOW_COPY_AND_ASSIGN(MessageLoopForIoPosixTest);
};
class TestHandler : public MessageLoopForIO::Watcher {
public:
void OnFileCanReadWithoutBlocking(int fd) override {
watcher_to_delete_ = nullptr;
is_readable_ = true;
RunLoop::QuitCurrentWhenIdleDeprecated();
}
void OnFileCanWriteWithoutBlocking(int fd) override {
watcher_to_delete_ = nullptr;
is_writable_ = true;
RunLoop::QuitCurrentWhenIdleDeprecated();
}
bool is_readable_ = false;
bool is_writable_ = false;
// If set then the contained watcher will be deleted on notification.
std::unique_ptr<MessageLoopForIO::FileDescriptorWatcher> watcher_to_delete_;
};
TEST_F(MessageLoopForIoPosixTest, FileDescriptorWatcherOutlivesMessageLoop) {
// Simulate a MessageLoop that dies before an FileDescriptorWatcher.
// This could happen when people use the Singleton pattern or atexit.
// Arrange for watcher to live longer than message loop.
MessageLoopForIO::FileDescriptorWatcher watcher(FROM_HERE);
TestHandler handler;
{
MessageLoopForIO message_loop;
message_loop.WatchFileDescriptor(write_fd_.GetPlatformFile(), true,
MessageLoopForIO::WATCH_WRITE, &watcher,
&handler);
// Don't run the message loop, just destroy it.
}
ASSERT_FALSE(handler.is_readable_);
ASSERT_FALSE(handler.is_writable_);
}
TEST_F(MessageLoopForIoPosixTest, FileDescriptorWatcherDoubleStop) {
// Verify that it's ok to call StopWatchingFileDescriptor().
// (Errors only showed up in valgrind.)
// Arrange for message loop to live longer than watcher.
MessageLoopForIO message_loop;
{
MessageLoopForIO::FileDescriptorWatcher watcher(FROM_HERE);
TestHandler handler;
message_loop.WatchFileDescriptor(write_fd_.GetPlatformFile(), true,
MessageLoopForIO::WATCH_WRITE, &watcher,
&handler);
ASSERT_TRUE(watcher.StopWatchingFileDescriptor());
ASSERT_TRUE(watcher.StopWatchingFileDescriptor());
}
}
TEST_F(MessageLoopForIoPosixTest, FileDescriptorWatcherDeleteInCallback) {
// Verify that it is OK to delete the FileDescriptorWatcher from within a
// callback.
MessageLoopForIO message_loop;
TestHandler handler;
handler.watcher_to_delete_ =
base::MakeUnique<MessageLoopForIO::FileDescriptorWatcher>(FROM_HERE);
message_loop.WatchFileDescriptor(write_fd_.GetPlatformFile(), true,
MessageLoopForIO::WATCH_WRITE,
handler.watcher_to_delete_.get(), &handler);
RunLoop().Run();
}
// Verify that basic readable notification works.
TEST_F(MessageLoopForIoPosixTest, WatchReadable) {
MessageLoopForIO message_loop;
MessageLoopForIO::FileDescriptorWatcher watcher(FROM_HERE);
TestHandler handler;
// Watch the pipe for readability.
ASSERT_TRUE(MessageLoopForIO::current()->WatchFileDescriptor(
read_fd_.GetPlatformFile(), /* persistent= */ false,
MessageLoopForIO::WATCH_READ, &watcher, &handler));
// The pipe should not be readable when first created.
base::RunLoop().RunUntilIdle();
ASSERT_FALSE(handler.is_readable_);
ASSERT_FALSE(handler.is_writable_);
// Write a byte to the other end, making it readable.
const char buf = 0;
ASSERT_TRUE(
WriteFileDescriptor(write_fd_.GetPlatformFile(), &buf, sizeof(buf)));
// We don't want to assume that the read fd becomes readable the
// instant a bytes is written, so Run until quit by an event.
RunLoop().Run();
ASSERT_TRUE(handler.is_readable_);
ASSERT_FALSE(handler.is_writable_);
}
// Verify that watching a file descriptor for writability succeeds.
TEST_F(MessageLoopForIoPosixTest, WatchWritable) {
MessageLoopForIO message_loop;
MessageLoopForIO::FileDescriptorWatcher watcher(FROM_HERE);
TestHandler handler;
// Watch the pipe for writability.
ASSERT_TRUE(MessageLoopForIO::current()->WatchFileDescriptor(
write_fd_.GetPlatformFile(), /* persistent= */ false,
MessageLoopForIO::WATCH_WRITE, &watcher, &handler));
// We should not receive a writable notification until we process events.
ASSERT_FALSE(handler.is_readable_);
ASSERT_FALSE(handler.is_writable_);
// The pipe should be writable immediately, but wait for the quit closure
// anyway, to be sure.
RunLoop().Run();
ASSERT_FALSE(handler.is_readable_);
ASSERT_TRUE(handler.is_writable_);
}
} // namespace
#endif // !defined(OS_NACL)
} // namespace base