// Copyright 2012 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_pump_io_ios.h"

#include <unistd.h>

#include "base/macros.h"
#include "base/posix/eintr_wrapper.h"
#include "base/test/gtest_util.h"
#include "base/threading/thread.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace base {

class MessagePumpIOSForIOTest : public testing::Test {
 protected:
  MessagePumpIOSForIOTest() = default;
  ~MessagePumpIOSForIOTest() override = default;

  void SetUp() override {
    int ret = pipe(pipefds_);
    ASSERT_EQ(0, ret);
    ret = pipe(alternate_pipefds_);
    ASSERT_EQ(0, ret);
  }

  void TearDown() override {
    if (IGNORE_EINTR(close(pipefds_[0])) < 0)
      PLOG(ERROR) << "close";
    if (IGNORE_EINTR(close(pipefds_[1])) < 0)
      PLOG(ERROR) << "close";
  }

  void HandleFdIOEvent(MessagePumpForIO::FdWatchController* watcher) {
    MessagePumpIOSForIO::HandleFdIOEvent(watcher->fdref_.get(),
        kCFFileDescriptorReadCallBack | kCFFileDescriptorWriteCallBack,
        watcher);
  }

  int pipefds_[2];
  int alternate_pipefds_[2];

 private:
  DISALLOW_COPY_AND_ASSIGN(MessagePumpIOSForIOTest);
};

namespace {

// Concrete implementation of MessagePumpIOSForIO::FdWatcher that does
// nothing useful.
class StupidWatcher : public MessagePumpIOSForIO::FdWatcher {
 public:
  ~StupidWatcher() override {}

  // base:MessagePumpIOSForIO::FdWatcher interface
  void OnFileCanReadWithoutBlocking(int fd) override {}
  void OnFileCanWriteWithoutBlocking(int fd) override {}
};

class BaseWatcher : public MessagePumpIOSForIO::FdWatcher {
 public:
  BaseWatcher(MessagePumpIOSForIO::FdWatchController* controller)
      : controller_(controller) {
    DCHECK(controller_);
  }
  ~BaseWatcher() override {}

  // MessagePumpIOSForIO::FdWatcher interface
  void OnFileCanReadWithoutBlocking(int /* fd */) override { NOTREACHED(); }

  void OnFileCanWriteWithoutBlocking(int /* fd */) override { NOTREACHED(); }

 protected:
  MessagePumpIOSForIO::FdWatchController* controller_;
};

class DeleteWatcher : public BaseWatcher {
 public:
  explicit DeleteWatcher(MessagePumpIOSForIO::FdWatchController* controller)
      : BaseWatcher(controller) {}

  ~DeleteWatcher() override { DCHECK(!controller_); }

  void OnFileCanWriteWithoutBlocking(int /* fd */) override {
    DCHECK(controller_);
    delete controller_;
    controller_ = NULL;
  }
};

TEST_F(MessagePumpIOSForIOTest, DeleteWatcher) {
  std::unique_ptr<MessagePumpIOSForIO> pump(new MessagePumpIOSForIO);
  MessagePumpIOSForIO::FdWatchController* watcher =
      new MessagePumpIOSForIO::FdWatchController(FROM_HERE);
  DeleteWatcher delegate(watcher);
  pump->WatchFileDescriptor(pipefds_[1],
      false, MessagePumpIOSForIO::WATCH_READ_WRITE, watcher, &delegate);

  // Spoof a callback.
  HandleFdIOEvent(watcher);
}

class StopWatcher : public BaseWatcher {
 public:
  StopWatcher(MessagePumpIOSForIO::FdWatchController* controller,
              MessagePumpIOSForIO* pump,
              int fd_to_start_watching = -1)
      : BaseWatcher(controller),
        pump_(pump),
        fd_to_start_watching_(fd_to_start_watching) {}

  ~StopWatcher() override {}

  void OnFileCanWriteWithoutBlocking(int /* fd */) override {
    controller_->StopWatchingFileDescriptor();
    if (fd_to_start_watching_ >= 0) {
      pump_->WatchFileDescriptor(fd_to_start_watching_,
          false, MessagePumpIOSForIO::WATCH_READ_WRITE, controller_, this);
    }
  }

 private:
  MessagePumpIOSForIO* pump_;
  int fd_to_start_watching_;
};

TEST_F(MessagePumpIOSForIOTest, StopWatcher) {
  std::unique_ptr<MessagePumpIOSForIO> pump(new MessagePumpIOSForIO);
  MessagePumpIOSForIO::FdWatchController watcher(FROM_HERE);
  StopWatcher delegate(&watcher, pump.get());
  pump->WatchFileDescriptor(pipefds_[1],
      false, MessagePumpIOSForIO::WATCH_READ_WRITE, &watcher, &delegate);

  // Spoof a callback.
  HandleFdIOEvent(&watcher);
}

TEST_F(MessagePumpIOSForIOTest, StopWatcherAndWatchSomethingElse) {
  std::unique_ptr<MessagePumpIOSForIO> pump(new MessagePumpIOSForIO);
  MessagePumpIOSForIO::FdWatchController watcher(FROM_HERE);
  StopWatcher delegate(&watcher, pump.get(), alternate_pipefds_[1]);
  pump->WatchFileDescriptor(pipefds_[1],
      false, MessagePumpIOSForIO::WATCH_READ_WRITE, &watcher, &delegate);

  // Spoof a callback.
  HandleFdIOEvent(&watcher);
}

}  // namespace

}  // namespace base
