Index: ipc/unix_domain_socket_util_unittest.cc |
diff --git a/ipc/unix_domain_socket_util_unittest.cc b/ipc/unix_domain_socket_util_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a00d3aae4837f80c1201f030889c3ef9ebad0c0a |
--- /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 |