// 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/message_loop/message_loop.h"

#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop_current.h"
#include "base/message_loop/message_pump_for_io.h"
#include "base/posix/eintr_wrapper.h"
#include "base/run_loop.h"
#include "base/test/gtest_util.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() = default;

  // 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_ = ScopedFD(pipefds[0]);
    write_fd_ = ScopedFD(pipefds[1]);
  }

  void TriggerReadEvent() {
    // Write from the other end of the pipe to trigger the event.
    char c = '\0';
    EXPECT_EQ(1, HANDLE_EINTR(write(write_fd_.get(), &c, 1)));
  }

 protected:
  ScopedFD read_fd_;
  ScopedFD write_fd_;

  DISALLOW_COPY_AND_ASSIGN(MessageLoopForIoPosixTest);
};

class TestHandler : public MessagePumpForIO::FdWatcher {
 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<MessagePumpForIO::FdWatchController> watcher_to_delete_;
};

// Watcher that calls specified closures when read/write events occur. Verifies
// that each non-null closure passed to this class is called once and only once.
// Also resets the read event by reading from the FD.
class CallClosureHandler : public MessagePumpForIO::FdWatcher {
 public:
  CallClosureHandler(OnceClosure read_closure, OnceClosure write_closure)
      : read_closure_(std::move(read_closure)),
        write_closure_(std::move(write_closure)) {}

  ~CallClosureHandler() override {
    EXPECT_TRUE(read_closure_.is_null());
    EXPECT_TRUE(write_closure_.is_null());
  }

  void SetReadClosure(OnceClosure read_closure) {
    EXPECT_TRUE(read_closure_.is_null());
    read_closure_ = std::move(read_closure);
  }

  void SetWriteClosure(OnceClosure write_closure) {
    EXPECT_TRUE(write_closure_.is_null());
    write_closure_ = std::move(write_closure);
  }

  // base:MessagePumpFuchsia::Watcher interface.
  void OnFileCanReadWithoutBlocking(int fd) override {
    // Empty the pipe buffer to reset the event. Otherwise libevent
    // implementation of MessageLoop may call the event handler again even if
    // |read_closure_| below quits the RunLoop.
    char c;
    int result = HANDLE_EINTR(read(fd, &c, 1));
    if (result == -1) {
      PLOG(ERROR) << "read";
      FAIL();
    }
    EXPECT_EQ(result, 1);

    ASSERT_FALSE(read_closure_.is_null());
    std::move(read_closure_).Run();
  }

  void OnFileCanWriteWithoutBlocking(int fd) override {
    ASSERT_FALSE(write_closure_.is_null());
    std::move(write_closure_).Run();
  }

 private:
  OnceClosure read_closure_;
  OnceClosure write_closure_;
};

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.
  MessagePumpForIO::FdWatchController watcher(FROM_HERE);
  TestHandler handler;
  {
    MessageLoopForIO message_loop;

    MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
        write_fd_.get(), true, MessagePumpForIO::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().

  // Arrange for message loop to live longer than watcher.
  MessageLoopForIO message_loop;
  {
    MessagePumpForIO::FdWatchController watcher(FROM_HERE);

    TestHandler handler;
    MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
        write_fd_.get(), true, MessagePumpForIO::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_ =
      std::make_unique<MessagePumpForIO::FdWatchController>(FROM_HERE);

  MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
      write_fd_.get(), true, MessagePumpForIO::WATCH_WRITE,
      handler.watcher_to_delete_.get(), &handler);
  RunLoop().Run();
}

// Verify that basic readable notification works.
TEST_F(MessageLoopForIoPosixTest, WatchReadable) {
  MessageLoopForIO message_loop;
  MessagePumpForIO::FdWatchController watcher(FROM_HERE);
  TestHandler handler;

  // Watch the pipe for readability.
  ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
      read_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_READ,
      &watcher, &handler));

  // The pipe should not be readable when first created.
  RunLoop().RunUntilIdle();
  ASSERT_FALSE(handler.is_readable_);
  ASSERT_FALSE(handler.is_writable_);

  TriggerReadEvent();

  // 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;
  MessagePumpForIO::FdWatchController watcher(FROM_HERE);
  TestHandler handler;

  // Watch the pipe for writability.
  ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
      write_fd_.get(), /*persistent=*/false, MessagePumpForIO::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_);
}

// Verify that RunUntilIdle() receives IO notifications.
TEST_F(MessageLoopForIoPosixTest, RunUntilIdle) {
  MessageLoopForIO message_loop;
  MessagePumpForIO::FdWatchController watcher(FROM_HERE);
  TestHandler handler;

  // Watch the pipe for readability.
  ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
      read_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_READ,
      &watcher, &handler));

  // The pipe should not be readable when first created.
  RunLoop().RunUntilIdle();
  ASSERT_FALSE(handler.is_readable_);

  TriggerReadEvent();

  while (!handler.is_readable_)
    RunLoop().RunUntilIdle();
}

void StopWatching(MessagePumpForIO::FdWatchController* controller,
                  RunLoop* run_loop) {
  controller->StopWatchingFileDescriptor();
  run_loop->Quit();
}

// Verify that StopWatchingFileDescriptor() works from an event handler.
TEST_F(MessageLoopForIoPosixTest, StopFromHandler) {
  MessageLoopForIO message_loop;
  RunLoop run_loop;
  MessagePumpForIO::FdWatchController watcher(FROM_HERE);
  CallClosureHandler handler(BindOnce(&StopWatching, &watcher, &run_loop),
                             OnceClosure());

  // Create persistent watcher.
  ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
      read_fd_.get(), /*persistent=*/true, MessagePumpForIO::WATCH_READ,
      &watcher, &handler));

  TriggerReadEvent();
  run_loop.Run();

  // Trigger the event again. The event handler should not be called again.
  TriggerReadEvent();
  RunLoop().RunUntilIdle();
}

// Verify that non-persistent watcher is called only once.
TEST_F(MessageLoopForIoPosixTest, NonPersistentWatcher) {
  MessageLoopForIO message_loop;
  MessagePumpForIO::FdWatchController watcher(FROM_HERE);

  RunLoop run_loop;
  CallClosureHandler handler(run_loop.QuitClosure(), OnceClosure());

  // Create a non-persistent watcher.
  ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
      read_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_READ,
      &watcher, &handler));

  TriggerReadEvent();
  run_loop.Run();

  // Trigger the event again. handler should not be called again.
  TriggerReadEvent();
  RunLoop().RunUntilIdle();
}

// Verify that persistent watcher is called every time the event is triggered.
TEST_F(MessageLoopForIoPosixTest, PersistentWatcher) {
  MessageLoopForIO message_loop;
  MessagePumpForIO::FdWatchController watcher(FROM_HERE);

  RunLoop run_loop1;
  CallClosureHandler handler(run_loop1.QuitClosure(), OnceClosure());

  // Create persistent watcher.
  ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
      read_fd_.get(), /*persistent=*/true, MessagePumpForIO::WATCH_READ,
      &watcher, &handler));

  TriggerReadEvent();
  run_loop1.Run();

  RunLoop run_loop2;
  handler.SetReadClosure(run_loop2.QuitClosure());

  // Trigger the event again. handler should be called now, which will quit
  // run_loop2.
  TriggerReadEvent();
  run_loop2.Run();
}

void StopWatchingAndWatchAgain(MessagePumpForIO::FdWatchController* controller,
                               int fd,
                               MessagePumpForIO::FdWatcher* new_handler,
                               RunLoop* run_loop) {
  controller->StopWatchingFileDescriptor();

  ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
      fd, /*persistent=*/true, MessagePumpForIO::WATCH_READ, controller,
      new_handler));

  run_loop->Quit();
}

// Verify that a watcher can be stopped and reused from an event handler.
TEST_F(MessageLoopForIoPosixTest, StopAndRestartFromHandler) {
  MessageLoopForIO message_loop;
  MessagePumpForIO::FdWatchController watcher(FROM_HERE);

  RunLoop run_loop1;
  RunLoop run_loop2;
  CallClosureHandler handler2(run_loop2.QuitClosure(), OnceClosure());
  CallClosureHandler handler1(BindOnce(&StopWatchingAndWatchAgain, &watcher,
                                       read_fd_.get(), &handler2, &run_loop1),
                              OnceClosure());

  // Create persistent watcher.
  ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
      read_fd_.get(), /*persistent=*/true, MessagePumpForIO::WATCH_READ,
      &watcher, &handler1));

  TriggerReadEvent();
  run_loop1.Run();

  // Trigger the event again. handler2 should be called now, which will quit
  // run_loop2
  TriggerReadEvent();
  run_loop2.Run();
}

// Verify that the pump properly handles a delayed task after an IO event.
TEST_F(MessageLoopForIoPosixTest, IoEventThenTimer) {
  MessageLoopForIO message_loop;
  MessagePumpForIO::FdWatchController watcher(FROM_HERE);

  RunLoop timer_run_loop;
  message_loop.task_runner()->PostDelayedTask(
      FROM_HERE, timer_run_loop.QuitClosure(),
      base::TimeDelta::FromMilliseconds(10));

  RunLoop watcher_run_loop;
  CallClosureHandler handler(watcher_run_loop.QuitClosure(), OnceClosure());

  // Create a non-persistent watcher.
  ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
      read_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_READ,
      &watcher, &handler));

  TriggerReadEvent();

  // Normally the IO event will be received before the delayed task is
  // executed, so this run loop will first handle the IO event and then quit on
  // the timer.
  timer_run_loop.Run();

  // Run watcher_run_loop in case the IO event wasn't received before the
  // delayed task.
  watcher_run_loop.Run();
}

// Verify that the pipe can handle an IO event after a delayed task.
TEST_F(MessageLoopForIoPosixTest, TimerThenIoEvent) {
  MessageLoopForIO message_loop;
  MessagePumpForIO::FdWatchController watcher(FROM_HERE);

  // Trigger read event from a delayed task.
  message_loop.task_runner()->PostDelayedTask(
      FROM_HERE,
      BindOnce(&MessageLoopForIoPosixTest::TriggerReadEvent, Unretained(this)),
      TimeDelta::FromMilliseconds(1));

  RunLoop run_loop;
  CallClosureHandler handler(run_loop.QuitClosure(), OnceClosure());

  // Create a non-persistent watcher.
  ASSERT_TRUE(MessageLoopCurrentForIO::Get()->WatchFileDescriptor(
      read_fd_.get(), /*persistent=*/false, MessagePumpForIO::WATCH_READ,
      &watcher, &handler));

  run_loop.Run();
}

}  // namespace

#endif  // !defined(OS_NACL)

}  // namespace base
