Implement IPC::ChannelFactory, a class that accept()s on a UNIX socket.

IPC::ChannelFactory listens on a UNIX domain socket and notifies its delegate
when a client connects. The delegate is expected to craft an IPC::Channel from
the handle it is given.

Previously committed:
- https://src.chromium.org/viewvc/chrome?view=rev&revision=186912
- https://src.chromium.org/viewvc/chrome?view=rev&revision=187233
- https://src.chromium.org/viewvc/chrome?view=rev&revision=187554

Review URL: https://codereview.chromium.org/12386010

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@187772 0039d316-1c4b-4281-b951-d872f2087c98


CrOS-Libchrome-Original-Commit: bdf9bdc258df1e56f7ac7e3de705b0a70e9f93e4
diff --git a/ipc/unix_domain_socket_util_unittest.cc b/ipc/unix_domain_socket_util_unittest.cc
new file mode 100644
index 0000000..a00d3aa
--- /dev/null
+++ b/ipc/unix_domain_socket_util_unittest.cc
@@ -0,0 +1,179 @@
+// Copyright 2013 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 <sys/socket.h>
+
+#include "base/bind.h"
+#include "base/file_util.h"
+#include "base/files/file_path.h"
+#include "base/path_service.h"
+#include "base/synchronization/waitable_event.h"
+#include "base/threading/thread.h"
+#include "base/threading/thread_restrictions.h"
+#include "ipc/unix_domain_socket_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace {
+
+class SocketAcceptor : public MessageLoopForIO::Watcher {
+ public:
+  SocketAcceptor(int fd, base::MessageLoopProxy* target_thread)
+      : server_fd_(-1),
+        target_thread_(target_thread),
+        started_watching_event_(false, false),
+        accepted_event_(false, false) {
+    target_thread->PostTask(FROM_HERE,
+        base::Bind(&SocketAcceptor::StartWatching, base::Unretained(this), fd));
+  }
+
+  virtual ~SocketAcceptor() {
+    Close();
+  }
+
+  int server_fd() const { return server_fd_; }
+
+  void WaitUntilReady() {
+    started_watching_event_.Wait();
+  }
+
+  void WaitForAccept() {
+    accepted_event_.Wait();
+  }
+
+  void Close() {
+    if (watcher_.get()) {
+      target_thread_->PostTask(FROM_HERE,
+          base::Bind(&SocketAcceptor::StopWatching, base::Unretained(this),
+              watcher_.release()));
+    }
+  }
+
+ private:
+  void StartWatching(int fd) {
+    watcher_.reset(new MessageLoopForIO::FileDescriptorWatcher);
+    MessageLoopForIO::current()->WatchFileDescriptor(
+        fd,
+        true,
+        MessageLoopForIO::WATCH_READ,
+        watcher_.get(),
+        this);
+    started_watching_event_.Signal();
+  }
+  void StopWatching(MessageLoopForIO::FileDescriptorWatcher* watcher) {
+    watcher->StopWatchingFileDescriptor();
+    delete watcher;
+  }
+  virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE {
+    ASSERT_EQ(-1, server_fd_);
+    IPC::ServerAcceptConnection(fd, &server_fd_);
+    watcher_->StopWatchingFileDescriptor();
+    accepted_event_.Signal();
+  }
+  virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE {}
+
+  int server_fd_;
+  base::MessageLoopProxy* target_thread_;
+  scoped_ptr<MessageLoopForIO::FileDescriptorWatcher> watcher_;
+  base::WaitableEvent started_watching_event_;
+  base::WaitableEvent accepted_event_;
+
+  DISALLOW_COPY_AND_ASSIGN(SocketAcceptor);
+};
+
+const base::FilePath GetChannelDir() {
+#if defined(OS_ANDROID)
+  base::FilePath tmp_dir;
+  PathService::Get(base::DIR_CACHE, &tmp_dir);
+  return tmp_dir;
+#else
+  return base::FilePath("/var/tmp");
+#endif
+}
+
+class TestUnixSocketConnection {
+ public:
+  TestUnixSocketConnection()
+      : worker_("WorkerThread"),
+        server_listen_fd_(-1),
+        server_fd_(-1),
+        client_fd_(-1) {
+    socket_name_ = GetChannelDir().Append("TestSocket");
+    base::Thread::Options options;
+    options.message_loop_type = MessageLoop::TYPE_IO;
+    worker_.StartWithOptions(options);
+  }
+
+  bool CreateServerSocket() {
+    IPC::CreateServerUnixDomainSocket(socket_name_, &server_listen_fd_);
+    if (server_listen_fd_ < 0)
+      return false;
+    struct stat socket_stat;
+    stat(socket_name_.value().c_str(), &socket_stat);
+    EXPECT_TRUE(S_ISSOCK(socket_stat.st_mode));
+    acceptor_.reset(new SocketAcceptor(server_listen_fd_,
+                                       worker_.message_loop_proxy()));
+    acceptor_->WaitUntilReady();
+    return true;
+  }
+
+  bool CreateClientSocket() {
+    DCHECK(server_listen_fd_ >= 0);
+    IPC::CreateClientUnixDomainSocket(socket_name_, &client_fd_);
+    if (client_fd_ < 0)
+      return false;
+    acceptor_->WaitForAccept();
+    server_fd_ = acceptor_->server_fd();
+    return server_fd_ >= 0;
+  }
+
+  virtual ~TestUnixSocketConnection() {
+    if (client_fd_ >= 0)
+      close(client_fd_);
+    if (server_fd_ >= 0)
+      close(server_fd_);
+    if (server_listen_fd_ >= 0) {
+      close(server_listen_fd_);
+      unlink(socket_name_.value().c_str());
+    }
+  }
+
+  int client_fd() const { return client_fd_; }
+  int server_fd() const { return server_fd_; }
+
+ private:
+  base::Thread worker_;
+  base::FilePath socket_name_;
+  int server_listen_fd_;
+  int server_fd_;
+  int client_fd_;
+  scoped_ptr<SocketAcceptor> acceptor_;
+};
+
+// Ensure that IPC::CreateServerUnixDomainSocket creates a socket that
+// IPC::CreateClientUnixDomainSocket can successfully connect to.
+TEST(UnixDomainSocketUtil, Connect) {
+  TestUnixSocketConnection connection;
+  ASSERT_TRUE(connection.CreateServerSocket());
+  ASSERT_TRUE(connection.CreateClientSocket());
+}
+
+// Ensure that messages can be sent across the resulting socket.
+TEST(UnixDomainSocketUtil, SendReceive) {
+  TestUnixSocketConnection connection;
+  ASSERT_TRUE(connection.CreateServerSocket());
+  ASSERT_TRUE(connection.CreateClientSocket());
+
+  const char buffer[] = "Hello, server!";
+  size_t buf_len = sizeof(buffer);
+  size_t sent_bytes =
+      HANDLE_EINTR(send(connection.client_fd(), buffer, buf_len, 0));
+  ASSERT_EQ(buf_len, sent_bytes);
+  char recv_buf[sizeof(buffer)];
+  size_t received_bytes =
+      HANDLE_EINTR(recv(connection.server_fd(), recv_buf, buf_len, 0));
+  ASSERT_EQ(buf_len, received_bytes);
+  ASSERT_EQ(0, memcmp(recv_buf, buffer, buf_len));
+}
+
+}  // namespace