1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/message_loop/message_pump_io_ios.h"
6
7#include <unistd.h>
8
9#include "base/message_loop/message_loop.h"
10#include "base/posix/eintr_wrapper.h"
11#include "base/threading/thread.h"
12#include "testing/gtest/include/gtest/gtest.h"
13
14namespace base {
15
16class MessagePumpIOSForIOTest : public testing::Test {
17 protected:
18  MessagePumpIOSForIOTest()
19      : ui_loop_(MessageLoop::TYPE_UI),
20        io_thread_("MessagePumpIOSForIOTestIOThread") {}
21  virtual ~MessagePumpIOSForIOTest() {}
22
23  virtual void SetUp() OVERRIDE {
24    Thread::Options options(MessageLoop::TYPE_IO, 0);
25    ASSERT_TRUE(io_thread_.StartWithOptions(options));
26    ASSERT_EQ(MessageLoop::TYPE_IO, io_thread_.message_loop()->type());
27    int ret = pipe(pipefds_);
28    ASSERT_EQ(0, ret);
29    ret = pipe(alternate_pipefds_);
30    ASSERT_EQ(0, ret);
31  }
32
33  virtual void TearDown() OVERRIDE {
34    if (IGNORE_EINTR(close(pipefds_[0])) < 0)
35      PLOG(ERROR) << "close";
36    if (IGNORE_EINTR(close(pipefds_[1])) < 0)
37      PLOG(ERROR) << "close";
38  }
39
40  MessageLoop* ui_loop() { return &ui_loop_; }
41  MessageLoopForIO* io_loop() const {
42    return static_cast<MessageLoopForIO*>(io_thread_.message_loop());
43  }
44
45  void HandleFdIOEvent(MessageLoopForIO::FileDescriptorWatcher* watcher) {
46    MessagePumpIOSForIO::HandleFdIOEvent(watcher->fdref_,
47        kCFFileDescriptorReadCallBack | kCFFileDescriptorWriteCallBack,
48        watcher);
49  }
50
51  int pipefds_[2];
52  int alternate_pipefds_[2];
53
54 private:
55  MessageLoop ui_loop_;
56  Thread io_thread_;
57
58  DISALLOW_COPY_AND_ASSIGN(MessagePumpIOSForIOTest);
59};
60
61namespace {
62
63// Concrete implementation of MessagePumpIOSForIO::Watcher that does
64// nothing useful.
65class StupidWatcher : public MessagePumpIOSForIO::Watcher {
66 public:
67  virtual ~StupidWatcher() {}
68
69  // base:MessagePumpIOSForIO::Watcher interface
70  virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {}
71  virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {}
72};
73
74#if GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
75
76// Test to make sure that we catch calling WatchFileDescriptor off of the
77//  wrong thread.
78TEST_F(MessagePumpIOSForIOTest, TestWatchingFromBadThread) {
79  MessagePumpIOSForIO::FileDescriptorWatcher watcher;
80  StupidWatcher delegate;
81
82  ASSERT_DEBUG_DEATH(io_loop()->WatchFileDescriptor(
83      STDOUT_FILENO, false, MessageLoopForIO::WATCH_READ, &watcher, &delegate),
84      "Check failed: "
85      "watch_file_descriptor_caller_checker_.CalledOnValidThread\\(\\)");
86}
87
88#endif  // GTEST_HAS_DEATH_TEST && !defined(NDEBUG)
89
90class BaseWatcher : public MessagePumpIOSForIO::Watcher {
91 public:
92  BaseWatcher(MessagePumpIOSForIO::FileDescriptorWatcher* controller)
93      : controller_(controller) {
94    DCHECK(controller_);
95  }
96  virtual ~BaseWatcher() {}
97
98  // MessagePumpIOSForIO::Watcher interface
99  virtual void OnFileCanReadWithoutBlocking(int /* fd */) OVERRIDE {
100    NOTREACHED();
101  }
102
103  virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE {
104    NOTREACHED();
105  }
106
107 protected:
108  MessagePumpIOSForIO::FileDescriptorWatcher* controller_;
109};
110
111class DeleteWatcher : public BaseWatcher {
112 public:
113  explicit DeleteWatcher(
114      MessagePumpIOSForIO::FileDescriptorWatcher* controller)
115      : BaseWatcher(controller) {}
116
117  virtual ~DeleteWatcher() {
118    DCHECK(!controller_);
119  }
120
121  virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE {
122    DCHECK(controller_);
123    delete controller_;
124    controller_ = NULL;
125  }
126};
127
128TEST_F(MessagePumpIOSForIOTest, DeleteWatcher) {
129  scoped_ptr<MessagePumpIOSForIO> pump(new MessagePumpIOSForIO);
130  MessagePumpIOSForIO::FileDescriptorWatcher* watcher =
131      new MessagePumpIOSForIO::FileDescriptorWatcher;
132  DeleteWatcher delegate(watcher);
133  pump->WatchFileDescriptor(pipefds_[1],
134      false, MessagePumpIOSForIO::WATCH_READ_WRITE, watcher, &delegate);
135
136  // Spoof a callback.
137  HandleFdIOEvent(watcher);
138}
139
140class StopWatcher : public BaseWatcher {
141 public:
142  StopWatcher(MessagePumpIOSForIO::FileDescriptorWatcher* controller,
143              MessagePumpIOSForIO* pump,
144              int fd_to_start_watching = -1)
145      : BaseWatcher(controller),
146        pump_(pump),
147        fd_to_start_watching_(fd_to_start_watching) {}
148
149  virtual ~StopWatcher() {}
150
151  virtual void OnFileCanWriteWithoutBlocking(int /* fd */) OVERRIDE {
152    controller_->StopWatchingFileDescriptor();
153    if (fd_to_start_watching_ >= 0) {
154      pump_->WatchFileDescriptor(fd_to_start_watching_,
155          false, MessagePumpIOSForIO::WATCH_READ_WRITE, controller_, this);
156    }
157  }
158
159 private:
160  MessagePumpIOSForIO* pump_;
161  int fd_to_start_watching_;
162};
163
164TEST_F(MessagePumpIOSForIOTest, StopWatcher) {
165  scoped_ptr<MessagePumpIOSForIO> pump(new MessagePumpIOSForIO);
166  MessagePumpIOSForIO::FileDescriptorWatcher watcher;
167  StopWatcher delegate(&watcher, pump.get());
168  pump->WatchFileDescriptor(pipefds_[1],
169      false, MessagePumpIOSForIO::WATCH_READ_WRITE, &watcher, &delegate);
170
171  // Spoof a callback.
172  HandleFdIOEvent(&watcher);
173}
174
175TEST_F(MessagePumpIOSForIOTest, StopWatcherAndWatchSomethingElse) {
176  scoped_ptr<MessagePumpIOSForIO> pump(new MessagePumpIOSForIO);
177  MessagePumpIOSForIO::FileDescriptorWatcher watcher;
178  StopWatcher delegate(&watcher, pump.get(), alternate_pipefds_[1]);
179  pump->WatchFileDescriptor(pipefds_[1],
180      false, MessagePumpIOSForIO::WATCH_READ_WRITE, &watcher, &delegate);
181
182  // Spoof a callback.
183  HandleFdIOEvent(&watcher);
184}
185
186}  // namespace
187
188}  // namespace base
189