- add sources.
[platform/framework/web/crosswalk.git] / src / net / test / spawned_test_server / local_test_server_posix.cc
1 // Copyright (c) 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 "net/test/spawned_test_server/local_test_server.h"
6
7 #include <poll.h>
8
9 #include <vector>
10
11 #include "base/command_line.h"
12 #include "base/file_util.h"
13 #include "base/logging.h"
14 #include "base/process/kill.h"
15 #include "base/process/launch.h"
16 #include "base/process/process_iterator.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_util.h"
19 #include "base/test/test_timeouts.h"
20 #include "net/test/python_utils.h"
21
22 namespace {
23
24 // Helper class used to detect and kill orphaned python test server processes.
25 // Checks if the command line of a process contains |path_string| (the path
26 // from which the test server was launched) and |port_string| (the port used by
27 // the test server), and if the parent pid of the process is 1 (indicating that
28 // it is an orphaned process).
29 class OrphanedTestServerFilter : public base::ProcessFilter {
30  public:
31   OrphanedTestServerFilter(
32       const std::string& path_string, const std::string& port_string)
33       : path_string_(path_string),
34         port_string_(port_string) {}
35
36   virtual bool Includes(const base::ProcessEntry& entry) const OVERRIDE {
37     if (entry.parent_pid() != 1)
38       return false;
39     bool found_path_string = false;
40     bool found_port_string = false;
41     for (std::vector<std::string>::const_iterator it =
42          entry.cmd_line_args().begin();
43          it != entry.cmd_line_args().end();
44          ++it) {
45       if (it->find(path_string_) != std::string::npos)
46         found_path_string = true;
47       if (it->find(port_string_) != std::string::npos)
48         found_port_string = true;
49     }
50     return found_path_string && found_port_string;
51   }
52
53  private:
54   std::string path_string_;
55   std::string port_string_;
56   DISALLOW_COPY_AND_ASSIGN(OrphanedTestServerFilter);
57 };
58
59 // Given a file descriptor, reads into |buffer| until |bytes_max|
60 // bytes has been read or an error has been encountered.  Returns true
61 // if the read was successful.  |remaining_time| is used as a timeout.
62 bool ReadData(int fd, ssize_t bytes_max, uint8* buffer,
63               base::TimeDelta* remaining_time) {
64   ssize_t bytes_read = 0;
65   base::TimeTicks previous_time = base::TimeTicks::Now();
66   while (bytes_read < bytes_max) {
67     struct pollfd poll_fds[1];
68
69     poll_fds[0].fd = fd;
70     poll_fds[0].events = POLLIN | POLLPRI;
71     poll_fds[0].revents = 0;
72
73     int rv = HANDLE_EINTR(poll(poll_fds, 1,
74                                remaining_time->InMilliseconds()));
75     if (rv == 0) {
76       LOG(ERROR) << "poll() timed out; bytes_read=" << bytes_read;
77       return false;
78     } else if (rv < 0) {
79       PLOG(ERROR) << "poll() failed for child file descriptor; bytes_read="
80                   << bytes_read;
81       return false;
82     }
83
84     base::TimeTicks current_time = base::TimeTicks::Now();
85     base::TimeDelta elapsed_time_cycle = current_time - previous_time;
86     DCHECK_GE(elapsed_time_cycle.InMilliseconds(), 0);
87     *remaining_time -= elapsed_time_cycle;
88     previous_time = current_time;
89
90     ssize_t num_bytes = HANDLE_EINTR(read(fd, buffer + bytes_read,
91                                           bytes_max - bytes_read));
92     if (num_bytes <= 0)
93       return false;
94     bytes_read += num_bytes;
95   }
96   return true;
97 }
98
99 }  // namespace
100
101 namespace net {
102
103 bool LocalTestServer::LaunchPython(const base::FilePath& testserver_path) {
104   // Log is useful in the event you want to run a nearby script (e.g. a test) in
105   // the same environment as the TestServer.
106   VLOG(1) << "LaunchPython called with PYTHONPATH = " << getenv(kPythonPathEnv);
107
108   CommandLine python_command(CommandLine::NO_PROGRAM);
109   if (!GetPythonCommand(&python_command))
110     return false;
111
112   python_command.AppendArgPath(testserver_path);
113   if (!AddCommandLineArguments(&python_command))
114     return false;
115
116   int pipefd[2];
117   if (pipe(pipefd) != 0) {
118     PLOG(ERROR) << "Could not create pipe.";
119     return false;
120   }
121
122   // Save the read half. The write half is sent to the child.
123   child_fd_ = pipefd[0];
124   child_fd_closer_.reset(&child_fd_);
125   file_util::ScopedFD write_closer(&pipefd[1]);
126   base::FileHandleMappingVector map_write_fd;
127   map_write_fd.push_back(std::make_pair(pipefd[1], pipefd[1]));
128
129   python_command.AppendArg("--startup-pipe=" + base::IntToString(pipefd[1]));
130
131   // Try to kill any orphaned testserver processes that may be running.
132   OrphanedTestServerFilter filter(testserver_path.value(),
133                                   base::IntToString(GetPort()));
134   if (!base::KillProcesses("python", -1, &filter)) {
135     LOG(WARNING) << "Failed to clean up older orphaned testserver instances.";
136   }
137
138   // Launch a new testserver process.
139   base::LaunchOptions options;
140
141   options.fds_to_remap = &map_write_fd;
142   if (!base::LaunchProcess(python_command, options, &process_handle_)) {
143     LOG(ERROR) << "Failed to launch " << python_command.GetCommandLineString();
144     return false;
145   }
146
147   return true;
148 }
149
150 bool LocalTestServer::WaitToStart() {
151   file_util::ScopedFD child_fd_closer(child_fd_closer_.release());
152
153   base::TimeDelta remaining_time = TestTimeouts::action_timeout();
154
155   uint32 server_data_len = 0;
156   if (!ReadData(child_fd_, sizeof(server_data_len),
157                 reinterpret_cast<uint8*>(&server_data_len),
158                 &remaining_time)) {
159     LOG(ERROR) << "Could not read server_data_len";
160     return false;
161   }
162   std::string server_data(server_data_len, '\0');
163   if (!ReadData(child_fd_, server_data_len,
164                 reinterpret_cast<uint8*>(&server_data[0]),
165                 &remaining_time)) {
166     LOG(ERROR) << "Could not read server_data (" << server_data_len
167                << " bytes)";
168     return false;
169   }
170
171   if (!ParseServerData(server_data)) {
172     LOG(ERROR) << "Could not parse server_data: " << server_data;
173     return false;
174   }
175
176   return true;
177 }
178
179 }  // namespace net