Allow to send multiple fds over UnixSocket.

Change-Id: Ie060ee7ef899f17e9e7d3631dd1267d1adff2021
diff --git a/src/ipc/unix_socket_unittest.cc b/src/ipc/unix_socket_unittest.cc
index 081a123..0e83f52 100644
--- a/src/ipc/unix_socket_unittest.cc
+++ b/src/ipc/unix_socket_unittest.cc
@@ -175,6 +175,91 @@
   task_runner_.RunUntilCheckpoint("srv_disconnected");
 }
 
+constexpr char cli_str[] = "cli>srv";
+constexpr char srv_str[] = "srv>cli";
+
+TEST_F(UnixSocketTest, ClientAndServerExchangeFDs) {
+  auto srv = UnixSocket::Listen(kSocketName, &event_listener_, &task_runner_);
+  ASSERT_TRUE(srv->is_listening());
+
+  auto cli = UnixSocket::Connect(kSocketName, &event_listener_, &task_runner_);
+  EXPECT_CALL(event_listener_, OnConnect(cli.get(), true));
+  auto cli_connected = task_runner_.CreateCheckpoint("cli_connected");
+  auto srv_disconnected = task_runner_.CreateCheckpoint("srv_disconnected");
+  EXPECT_CALL(event_listener_, OnNewIncomingConnection(srv.get(), _))
+      .WillOnce(Invoke([this, cli_connected, srv_disconnected](
+                           UnixSocket*, UnixSocket* srv_conn) {
+        EXPECT_CALL(event_listener_, OnDisconnect(srv_conn))
+            .WillOnce(InvokeWithoutArgs(srv_disconnected));
+        cli_connected();
+      }));
+  task_runner_.RunUntilCheckpoint("cli_connected");
+
+  auto srv_conn = event_listener_.GetIncomingConnection();
+  ASSERT_TRUE(srv_conn);
+  ASSERT_TRUE(cli->is_connected());
+
+  base::ScopedFile null_fd(open("/dev/null", O_RDONLY));
+  base::ScopedFile zero_fd(open("/dev/zero", O_RDONLY));
+
+  auto cli_did_recv = task_runner_.CreateCheckpoint("cli_did_recv");
+  EXPECT_CALL(event_listener_, OnDataAvailable(cli.get()))
+      .WillRepeatedly(Invoke([cli_did_recv](UnixSocket* s) {
+        base::ScopedFile fd_buf[3];
+        char buf[sizeof(cli_str)];
+        if (!s->Receive(buf, sizeof(buf), fd_buf, base::ArraySize(fd_buf)))
+          return;
+        ASSERT_STREQ(srv_str, buf);
+        ASSERT_NE(*fd_buf[0], -1);
+        ASSERT_NE(*fd_buf[1], -1);
+        ASSERT_EQ(*fd_buf[2], -1);
+
+        char rd_buf[1];
+        // /dev/null
+        ASSERT_EQ(read(*fd_buf[0], rd_buf, sizeof(rd_buf)), 0);
+        // /dev/zero
+        ASSERT_EQ(read(*fd_buf[1], rd_buf, sizeof(rd_buf)), 1);
+        cli_did_recv();
+      }));
+
+  auto srv_did_recv = task_runner_.CreateCheckpoint("srv_did_recv");
+  EXPECT_CALL(event_listener_, OnDataAvailable(srv_conn.get()))
+      .WillRepeatedly(Invoke([srv_did_recv](UnixSocket* s) {
+        base::ScopedFile fd_buf[3];
+        char buf[sizeof(srv_str)];
+        if (!s->Receive(buf, sizeof(buf), fd_buf, base::ArraySize(fd_buf)))
+          return;
+        ASSERT_STREQ(cli_str, buf);
+        ASSERT_NE(*fd_buf[0], -1);
+        ASSERT_NE(*fd_buf[1], -1);
+        ASSERT_EQ(*fd_buf[2], -1);
+
+        char rd_buf[1];
+        // /dev/null
+        ASSERT_EQ(read(*fd_buf[0], rd_buf, sizeof(rd_buf)), 0);
+        // /dev/zero
+        ASSERT_EQ(read(*fd_buf[1], rd_buf, sizeof(rd_buf)), 1);
+        srv_did_recv();
+      }));
+
+  int buf_fd[2] = {null_fd.get(), zero_fd.get()};
+
+  ASSERT_TRUE(cli->Send(cli_str, sizeof(cli_str), buf_fd,
+                        base::ArraySize(buf_fd)));
+  ASSERT_TRUE(srv_conn->Send(srv_str, sizeof(srv_str), buf_fd,
+                             base::ArraySize(buf_fd)));
+  task_runner_.RunUntilCheckpoint("srv_did_recv");
+  task_runner_.RunUntilCheckpoint("cli_did_recv");
+
+  auto cli_disconnected = task_runner_.CreateCheckpoint("cli_disconnected");
+  EXPECT_CALL(event_listener_, OnDisconnect(cli.get()))
+      .WillOnce(InvokeWithoutArgs(cli_disconnected));
+  cli->Shutdown(true);
+  srv->Shutdown(true);
+  task_runner_.RunUntilCheckpoint("srv_disconnected");
+  task_runner_.RunUntilCheckpoint("cli_disconnected");
+}
+
 TEST_F(UnixSocketTest, ListenWithPassedFileDescriptor) {
   auto fd = UnixSocket::CreateAndBind(kSocketName);
   auto srv = UnixSocket::Listen(std::move(fd), &event_listener_, &task_runner_);