port_server_unittest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// Copyright (c) 2013 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 <string>
6
7#include "base/bind.h"
8#include "base/guid.h"
9#include "base/location.h"
10#include "base/message_loop/message_loop.h"
11#include "base/sync_socket.h"
12#include "base/synchronization/waitable_event.h"
13#include "base/threading/thread.h"
14#include "base/time/time.h"
15#include "chrome/test/chromedriver/chrome/status.h"
16#include "chrome/test/chromedriver/net/port_server.h"
17#include "net/base/sys_addrinfo.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
20#if defined(OS_LINUX)
21#include <fcntl.h>
22#include <sys/socket.h>
23#include <sys/un.h>
24#endif
25
26namespace {
27
28void SetOnCall(bool* called) {
29  *called = true;
30}
31
32}  // namespace
33
34TEST(PortReservationTest, Normal) {
35  bool called = false;
36  {
37    PortReservation r(base::Bind(&SetOnCall, &called), 100);
38  }
39  ASSERT_TRUE(called);
40}
41
42TEST(PortReservationTest, Leak) {
43  bool called = false;
44  {
45    PortReservation r(base::Bind(&SetOnCall, &called), 100);
46    r.Leak();
47  }
48  ASSERT_FALSE(called);
49}
50
51TEST(PortReservationTest, MultipleLeaks) {
52  bool called = false;
53  {
54    PortReservation r(base::Bind(&SetOnCall, &called), 100);
55    r.Leak();
56    r.Leak();
57  }
58  ASSERT_FALSE(called);
59}
60
61#if defined(OS_LINUX)
62namespace {
63
64void RunServerOnThread(const std::string& path,
65                       const std::string& response,
66                       base::WaitableEvent* listen_event,
67                       std::string* request) {
68  int server_sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
69  ASSERT_GE(server_sock_fd, 0);
70  ASSERT_GE(fcntl(server_sock_fd, F_SETFL, O_NONBLOCK), 0);
71  base::SyncSocket server_sock(server_sock_fd);
72
73  struct sockaddr_un addr;
74  memset(&addr, 0, sizeof(addr));
75  addr.sun_family = AF_UNIX;
76  memcpy(addr.sun_path, &path[0], path.length());
77  ASSERT_EQ(0,
78            bind(server_sock_fd,
79                 reinterpret_cast<struct sockaddr*>(&addr),
80                 sizeof(sa_family_t) + path.length()));
81  ASSERT_EQ(0, listen(server_sock_fd, 1));
82  listen_event->Signal();
83
84  struct sockaddr_un  cli_addr;
85  socklen_t clilen = sizeof(cli_addr);
86  base::TimeTicks deadline =
87      base::TimeTicks::Now() + base::TimeDelta::FromSeconds(2);
88  int client_sock_fd = -1;
89  while (base::TimeTicks::Now() < deadline && client_sock_fd < 0) {
90    client_sock_fd = accept(
91        server_sock_fd, reinterpret_cast<struct sockaddr*>(&cli_addr), &clilen);
92  }
93  ASSERT_GE(client_sock_fd, 0);
94  base::SyncSocket sock(client_sock_fd);
95  do {
96    char c = 0;
97    size_t rv = sock.Receive(&c, 1);
98    if (!rv)
99      break;
100    request->push_back(c);
101  } while (sock.Peek());
102  sock.Send(response.c_str(), response.length());
103}
104
105std::string GenerateRandomPath() {
106  std::string path = base::GenerateGUID();
107  if (!path.empty()) {
108    std::string pre_path;
109    pre_path.push_back(0);  // Linux abstract namespace.
110    path = pre_path + path;
111  }
112  return path;
113}
114
115}  // namespace
116
117class PortServerTest : public testing::Test {
118 public:
119  PortServerTest() : thread_("server") {
120    EXPECT_TRUE(thread_.Start());
121  }
122
123  void RunServer(const std::string& path,
124                 const std::string& response,
125                 std::string* request) {
126    base::WaitableEvent listen_event(false, false);
127    thread_.message_loop()->PostTask(
128        FROM_HERE,
129        base::Bind(
130            &RunServerOnThread, path, response, &listen_event, request));
131    ASSERT_TRUE(listen_event.TimedWait(base::TimeDelta::FromSeconds(5)));
132  }
133
134 private:
135  base::Thread thread_;
136};
137
138TEST_F(PortServerTest, Reserve) {
139  std::string path = GenerateRandomPath();
140  PortServer server(path);
141
142  std::string request;
143  RunServer(path, "12345\n", &request);
144
145  int port = 0;
146  scoped_ptr<PortReservation> reservation;
147  Status status = server.ReservePort(&port, &reservation);
148  ASSERT_EQ(kOk, status.code()) << status.message();
149  ASSERT_EQ(port, 12345);
150}
151
152TEST_F(PortServerTest, ReserveResetReserve) {
153  std::string path = GenerateRandomPath();
154  PortServer server(path);
155
156  std::string request;
157  RunServer(path, "12345\n", &request);
158
159  int port = 0;
160  scoped_ptr<PortReservation> reservation;
161  Status status = server.ReservePort(&port, &reservation);
162  ASSERT_EQ(kOk, status.code()) << status.message();
163  ASSERT_EQ(port, 12345);
164
165  reservation.reset();
166  status = server.ReservePort(&port, &reservation);
167  ASSERT_EQ(kOk, status.code()) << status.message();
168  ASSERT_EQ(port, 12345);
169}
170
171TEST_F(PortServerTest, ReserveReserve) {
172  std::string path = GenerateRandomPath();
173  PortServer server(path);
174
175  std::string request;
176  RunServer(path, "12345\n", &request);
177
178  int port = 0;
179  scoped_ptr<PortReservation> reservation;
180  Status status = server.ReservePort(&port, &reservation);
181  ASSERT_EQ(kOk, status.code()) << status.message();
182  ASSERT_EQ(port, 12345);
183
184  RunServer(path, "12346\n", &request);
185  status = server.ReservePort(&port, &reservation);
186  ASSERT_EQ(kOk, status.code()) << status.message();
187  ASSERT_EQ(port, 12346);
188}
189#endif
190
191TEST(PortManagerTest, ReservePort) {
192  PortManager mgr(15000, 16000);
193  int port = 0;
194  scoped_ptr<PortReservation> reservation;
195  Status status = mgr.ReservePort(&port, &reservation);
196  ASSERT_EQ(kOk, status.code()) << status.message();
197
198  ASSERT_GE(port, 15000);
199  ASSERT_LE(port, 16000);
200  ASSERT_TRUE(reservation);
201}
202
203TEST(PortManagerTest, ReservePortFromPool) {
204  PortManager mgr(15000, 16000);
205  int first_port = 0, port = 1;
206  for (int i = 0; i < 10; i++) {
207    scoped_ptr<PortReservation> reservation;
208    Status status = mgr.ReservePortFromPool(&port, &reservation);
209    ASSERT_EQ(kOk, status.code()) << status.message();
210    ASSERT_TRUE(reservation);
211    ASSERT_GE(port, 15000);
212    ASSERT_LE(port, 16000);
213    if (i == 0)
214      first_port = port;
215    ASSERT_EQ(port, first_port);
216  }
217}
218