Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(589)

Side by Side Diff: build/android/pylib/forwarder.py

Issue 11269036: Support HTTP test-server based net unit tests on Android. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix Clang build + sync Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import logging 5 import logging
6 import os 6 import os
7 import re 7 import re
8 import sys 8 import sys
9 import time 9 import time
10 10
11 import android_commands 11 import android_commands
12 import cmd_helper 12 import cmd_helper
13 import constants 13 import constants
14 import ports 14 import ports
15 15
16 from pylib import pexpect 16 from pylib import pexpect
17 17
18
19 def _MakeBinaryPath(build_type, binary_name):
20 return os.path.join(constants.CHROME_DIR, 'out', build_type, binary_name)
21
22
18 class Forwarder(object): 23 class Forwarder(object):
19 """Class to manage port forwards from the device to the host.""" 24 """Class to manage port forwards from the device to the host."""
20 25
21 _DEVICE_FORWARDER_PATH = constants.TEST_EXECUTABLE_DIR + '/device_forwarder' 26 _DEVICE_FORWARDER_PATH = constants.TEST_EXECUTABLE_DIR + '/device_forwarder'
22 27
23 # Unix Abstract socket path: 28 # Unix Abstract socket path:
24 _DEVICE_ADB_CONTROL_PORT = 'chrome_device_forwarder' 29 _DEVICE_ADB_CONTROL_PORT = 'chrome_device_forwarder'
25 _TIMEOUT_SECS = 30 30 _TIMEOUT_SECS = 30
26 31
27 def __init__(self, adb, port_pairs, tool, host_name, build_type): 32 def __init__(self, adb, build_type):
28 """Forwards TCP ports on the device back to the host. 33 """Forwards TCP ports on the device back to the host.
29 34
30 Works like adb forward, but in reverse. 35 Works like adb forward, but in reverse.
31 36
32 Args: 37 Args:
33 adb: Instance of AndroidCommands for talking to the device. 38 adb: Instance of AndroidCommands for talking to the device.
39 build_type: 'Release' or 'Debug'.
40 """
41 assert build_type in ('Release', 'Debug')
42 self._adb = adb
43 self._host_to_device_port_map = dict()
44 self._device_process = None
45 self._host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder')
46 self._device_forwarder_path = _MakeBinaryPath(
47 build_type, 'device_forwarder')
48
49 def Run(self, port_pairs, tool, host_name):
50 """Runs the forwarder.
51
52 Args:
34 port_pairs: A list of tuples (device_port, host_port) to forward. Note 53 port_pairs: A list of tuples (device_port, host_port) to forward. Note
35 that you can specify 0 as a device_port, in which case a 54 that you can specify 0 as a device_port, in which case a
36 port will by dynamically assigned on the device. You can 55 port will by dynamically assigned on the device. You can
37 get the number of the assigned port using the 56 get the number of the assigned port using the
38 DevicePortForHostPort method. 57 DevicePortForHostPort method.
39 tool: Tool class to use to get wrapper, if necessary, for executing the 58 tool: Tool class to use to get wrapper, if necessary, for executing the
40 forwarder (see valgrind_tools.py). 59 forwarder (see valgrind_tools.py).
41 host_name: Address to forward to, must be addressable from the 60 host_name: Address to forward to, must be addressable from the
42 host machine. Usually use loopback '127.0.0.1'. 61 host machine. Usually use loopback '127.0.0.1'.
43 build_type: 'Release' or 'Debug'.
44 62
45 Raises: 63 Raises:
46 Exception on failure to forward the port. 64 Exception on failure to forward the port.
47 """ 65 """
48 self._adb = adb 66 host_adb_control_port = ports.AllocateTestServerPort()
49 self._host_to_device_port_map = dict() 67 if not host_adb_control_port:
50 self._host_process = None 68 raise Exception('Failed to allocate a TCP port in the host machine.')
51 self._device_process = None 69 self._adb.PushIfNeeded(self._device_forwarder_path,
52 self._adb_forward_process = None 70 Forwarder._DEVICE_FORWARDER_PATH)
71 redirection_commands = [
72 '%d:%d:%d:%s' % (host_adb_control_port, device, host,
73 host_name) for device, host in port_pairs]
74 logging.info('Command format: <ADB port>:<Device port>' +
75 '[:<Forward to port>:<Forward to address>]')
76 logging.info('Forwarding using commands: %s', redirection_commands)
77 if cmd_helper.RunCmd(
78 ['adb', '-s', self._adb._adb.GetSerialNumber(), 'forward',
79 'tcp:%s' % host_adb_control_port,
80 'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT]) != 0:
81 raise Exception('Error while running adb forward.')
53 82
54 self._host_adb_control_port = ports.AllocateTestServerPort() 83 if not self._adb.ExtractPid('device_forwarder'):
55 if not self._host_adb_control_port: 84 # TODO(pliard): Get rid of pexpect here and make device_forwarder a daemon
56 raise Exception('Failed to allocate a TCP port in the host machine.') 85 # with a blocking CLI process that exits with a proper exit code and not
57 adb.PushIfNeeded( 86 # while the daemon is still setting up. This would be similar to how
58 os.path.join(constants.CHROME_DIR, 'out', build_type, 87 # host_forwarder works.
59 'device_forwarder'), 88 self._device_process = pexpect.spawn(
60 Forwarder._DEVICE_FORWARDER_PATH) 89 'adb', ['-s',
61 self._host_forwarder_path = os.path.join(constants.CHROME_DIR, 90 self._adb._adb.GetSerialNumber(),
62 'out', 91 'shell',
63 build_type, 92 '%s %s -D --adb_sock=%s' % (
64 'host_forwarder') 93 tool.GetUtilWrapper(),
65 forward_string = ['%d:%d:%s' % 94 Forwarder._DEVICE_FORWARDER_PATH,
66 (device, host, host_name) for device, host in port_pairs] 95 Forwarder._DEVICE_ADB_CONTROL_PORT)])
67 logging.info('Forwarding ports: %s', forward_string) 96 device_success_re = re.compile('Starting Device Forwarder.')
97 device_failure_re = re.compile('.*:ERROR:(.*)')
98 index = self._device_process.expect([device_success_re,
99 device_failure_re,
100 pexpect.EOF,
101 pexpect.TIMEOUT],
102 Forwarder._TIMEOUT_SECS)
103 if index == 1:
104 error_msg = str(self._device_process.match.group(1))
105 logging.error(self._device_process.before)
106 self._device_process.close()
107 raise Exception('Failed to start Device Forwarder with Error: %s' %
108 error_msg)
109 elif index == 2:
110 logging.error(self._device_process.before)
111 self._device_process.close()
112 raise Exception(
113 'Unexpected EOF while trying to start Device Forwarder.')
114 elif index == 3:
115 logging.error(self._device_process.before)
116 self._device_process.close()
117 raise Exception('Timeout while trying start Device Forwarder')
118
119 for redirection_command in redirection_commands:
120 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
121 [self._host_forwarder_path, redirection_command])
122 if exit_code != 0:
123 raise Exception('%s exited with %d: %s' % (self._host_forwarder_path,
124 exit_code, output))
125 tokens = output.split(':')
126 if len(tokens) != 2:
127 raise Exception('Unexpected host forwarder output "%s", ' +
128 'expected "device_port:host_port"' % output)
129 device_port = int(tokens[0])
130 host_port = int(tokens[1])
131 self._host_to_device_port_map[host_port] = device_port
132 logging.info('Forwarding device port: %d to host port: %d.', device_port,
133 host_port)
134
135 @staticmethod
136 def KillHost(build_type):
137 host_forwarder_path = _MakeBinaryPath(build_type, 'host_forwarder')
138 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
139 [host_forwarder_path, 'kill-server'])
140 if exit_code != 0:
141 raise Exception('%s exited with %d: %s' % (host_forwarder_path,
142 exit_code, output))
143
144 @staticmethod
145 def KillDevice(adb):
146 logging.info('Killing device_forwarder.')
68 timeout_sec = 5 147 timeout_sec = 5
69 host_pattern = 'host_forwarder.*' + ' '.join(forward_string) 148 processes_killed = adb.KillAllBlocking('device_forwarder', timeout_sec)
70 # TODO(felipeg): Rather than using a blocking kill() here, the device
71 # forwarder could try to bind the Unix Domain Socket until it succeeds or
72 # while it fails because the socket is already bound (with appropriate
73 # timeout handling obviously).
74 self._KillHostForwarderBlocking(host_pattern, timeout_sec)
75 self._KillDeviceForwarderBlocking(timeout_sec)
76 self._adb_forward_process = pexpect.spawn(
77 'adb', ['-s',
78 adb._adb.GetSerialNumber(),
79 'forward',
80 'tcp:%s' % self._host_adb_control_port,
81 'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT])
82 self._device_process = pexpect.spawn(
83 'adb', ['-s',
84 adb._adb.GetSerialNumber(),
85 'shell',
86 '%s %s -D --adb_sock=%s' % (
87 tool.GetUtilWrapper(),
88 Forwarder._DEVICE_FORWARDER_PATH,
89 Forwarder._DEVICE_ADB_CONTROL_PORT)])
90
91 device_success_re = re.compile('Starting Device Forwarder.')
92 device_failure_re = re.compile('.*:ERROR:(.*)')
93 index = self._device_process.expect([device_success_re,
94 device_failure_re,
95 pexpect.EOF,
96 pexpect.TIMEOUT],
97 Forwarder._TIMEOUT_SECS)
98 if index == 1:
99 # Failure
100 error_msg = str(self._device_process.match.group(1))
101 logging.error(self._device_process.before)
102 self._CloseProcess()
103 raise Exception('Failed to start Device Forwarder with Error: %s' %
104 error_msg)
105 elif index == 2:
106 logging.error(self._device_process.before)
107 self._CloseProcess()
108 raise Exception('Unexpected EOF while trying to start Device Forwarder.')
109 elif index == 3:
110 logging.error(self._device_process.before)
111 self._CloseProcess()
112 raise Exception('Timeout while trying start Device Forwarder')
113
114 self._host_process = pexpect.spawn(self._host_forwarder_path,
115 ['--adb_port=%s' % (
116 self._host_adb_control_port)] +
117 forward_string)
118
119 # Read the output of the command to determine which device ports where
120 # forwarded to which host ports (necessary if
121 host_success_re = re.compile('Forwarding device port (\d+) to host (\d+):')
122 host_failure_re = re.compile('Couldn\'t start forwarder server for port '
123 'spec: (\d+):(\d+)')
124 for pair in port_pairs:
125 index = self._host_process.expect([host_success_re,
126 host_failure_re,
127 pexpect.EOF,
128 pexpect.TIMEOUT],
129 Forwarder._TIMEOUT_SECS)
130 if index == 0:
131 # Success
132 device_port = int(self._host_process.match.group(1))
133 host_port = int(self._host_process.match.group(2))
134 self._host_to_device_port_map[host_port] = device_port
135 logging.info("Forwarding device port: %d to host port: %d." %
136 (device_port, host_port))
137 elif index == 1:
138 # Failure
139 device_port = int(self._host_process.match.group(1))
140 host_port = int(self._host_process.match.group(2))
141 self._CloseProcess()
142 raise Exception('Failed to forward port %d to %d' % (device_port,
143 host_port))
144 elif index == 2:
145 logging.error(self._host_process.before)
146 self._CloseProcess()
147 raise Exception('Unexpected EOF while trying to forward ports %s' %
148 port_pairs)
149 elif index == 3:
150 logging.error(self._host_process.before)
151 self._CloseProcess()
152 raise Exception('Timeout while trying to forward ports %s' % port_pairs)
153
154 def _KillHostForwarderBlocking(self, host_pattern, timeout_sec):
155 """Kills any existing host forwarders using the provided pattern.
156
157 Note that this waits until the process terminates.
158 """
159 cmd_helper.RunCmd(['pkill', '-f', host_pattern])
160 elapsed = 0
161 wait_period = 0.1
162 while not cmd_helper.RunCmd(['pgrep', '-f', host_pattern]) and (
163 elapsed < timeout_sec):
164 time.sleep(wait_period)
165 elapsed += wait_period
166 if elapsed >= timeout_sec:
167 raise Exception('Timed out while killing ' + host_pattern)
168
169 def _KillDeviceForwarderBlocking(self, timeout_sec):
170 """Kills any existing device forwarders.
171
172 Note that this waits until the process terminates.
173 """
174 processes_killed = self._adb.KillAllBlocking(
175 'device_forwarder', timeout_sec)
176 if not processes_killed: 149 if not processes_killed:
177 pids = self._adb.ExtractPid('device_forwarder') 150 pids = adb.ExtractPid('device_forwarder')
178 if pids: 151 if pids:
179 raise Exception('Timed out while killing device_forwarder') 152 raise Exception('Timed out while killing device_forwarder')
180 153
181 def _CloseProcess(self):
182 if self._host_process:
183 self._host_process.close()
184 if self._device_process:
185 self._device_process.close()
186 if self._adb_forward_process:
187 self._adb_forward_process.close()
188 self._host_process = None
189 self._device_process = None
190 self._adb_forward_process = None
191
192 def DevicePortForHostPort(self, host_port): 154 def DevicePortForHostPort(self, host_port):
193 """Get the device port that corresponds to a given host port.""" 155 """Get the device port that corresponds to a given host port."""
194 return self._host_to_device_port_map.get(host_port) 156 return self._host_to_device_port_map.get(host_port)
195 157
196 def Close(self): 158 def Close(self):
197 """Terminate the forwarder process.""" 159 """Terminate the forwarder process."""
198 self._CloseProcess() 160 if self._device_process:
161 self._device_process.close()
162 self._device_process = None
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698