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

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: Revert changes in net_unittests_disabled 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 class Forwarder(object): 18 class Forwarder(object):
19 """Class to manage port forwards from the device to the host.""" 19 """Class to manage port forwards from the device to the host."""
20 20
21 _DEVICE_FORWARDER_PATH = constants.TEST_EXECUTABLE_DIR + '/device_forwarder' 21 _DEVICE_FORWARDER_PATH = constants.TEST_EXECUTABLE_DIR + '/device_forwarder'
22 22
23 # Unix Abstract socket path: 23 # Unix Abstract socket path:
24 _DEVICE_ADB_CONTROL_PORT = 'chrome_device_forwarder' 24 _DEVICE_ADB_CONTROL_PORT = 'chrome_device_forwarder'
25 _TIMEOUT_SECS = 30 25 _TIMEOUT_SECS = 30
26 26
27 def __init__(self, adb, port_pairs, tool, host_name, build_type): 27 def __init__(self, adb, build_type):
28 """Forwards TCP ports on the device back to the host. 28 """Forwards TCP ports on the device back to the host.
29 29
30 Works like adb forward, but in reverse. 30 Works like adb forward, but in reverse.
31 31
32 Args: 32 Args:
33 adb: Instance of AndroidCommands for talking to the device. 33 adb: Instance of AndroidCommands for talking to the device.
34 build_type: 'Release' or 'Debug'.
felipeg 2012/10/30 22:37:30 can you add an assert(build_type == Release or Deb
Philippe 2012/10/31 10:17:41 Done.
35 """
36 self._adb = adb
37 self._host_to_device_port_map = dict()
38 self._device_process = None
39 self._host_forwarder_path = os.path.join(
40 constants.CHROME_DIR, 'out', build_type, 'host_forwarder')
41 self._device_forwarder_path = os.path.join(
42 constants.CHROME_DIR, 'out', build_type, 'device_forwarder')
43
44 def Run(self, port_pairs, tool, host_name):
45 """Runs the forwarder.
46
47 Args:
34 port_pairs: A list of tuples (device_port, host_port) to forward. Note 48 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 49 that you can specify 0 as a device_port, in which case a
36 port will by dynamically assigned on the device. You can 50 port will by dynamically assigned on the device. You can
37 get the number of the assigned port using the 51 get the number of the assigned port using the
38 DevicePortForHostPort method. 52 DevicePortForHostPort method.
39 tool: Tool class to use to get wrapper, if necessary, for executing the 53 tool: Tool class to use to get wrapper, if necessary, for executing the
40 forwarder (see valgrind_tools.py). 54 forwarder (see valgrind_tools.py).
41 host_name: Address to forward to, must be addressable from the 55 host_name: Address to forward to, must be addressable from the
42 host machine. Usually use loopback '127.0.0.1'. 56 host machine. Usually use loopback '127.0.0.1'.
43 build_type: 'Release' or 'Debug'.
44 57
45 Raises: 58 Raises:
46 Exception on failure to forward the port. 59 Exception on failure to forward the port.
47 """ 60 """
48 self._adb = adb 61 host_adb_control_port = ports.AllocateTestServerPort()
49 self._host_to_device_port_map = dict() 62 if not host_adb_control_port:
50 self._host_process = None 63 raise Exception('Failed to allocate a TCP port in the host machine.')
51 self._device_process = None 64 self._adb.PushIfNeeded(self._device_forwarder_path,
52 self._adb_forward_process = None 65 Forwarder._DEVICE_FORWARDER_PATH)
66 redirection_commands = [
67 '%d:%d:%d:%s' % (host_adb_control_port, device, host,
68 host_name) for device, host in port_pairs]
69 logging.info('Command format: <ADB port>:<Device port>' +
70 '[:<Forward to port>:<Forward to address>]')
71 logging.info('Forwarding using commands: %s', redirection_commands)
72 if cmd_helper.RunCmd(
73 ['adb', '-s', self._adb._adb.GetSerialNumber(), 'forward',
74 'tcp:%s' % host_adb_control_port,
75 'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT]) != 0:
76 raise Exception('Error while running adb forward.')
53 77
54 self._host_adb_control_port = ports.AllocateTestServerPort() 78 if not self._adb.ExtractPid('device_forwarder'):
55 if not self._host_adb_control_port: 79 # 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.') 80 # with a blocking CLI process that exits with a proper exit code and not
57 adb.PushIfNeeded( 81 # while the daemon is still setting up. This would be similar to how
58 os.path.join(constants.CHROME_DIR, 'out', build_type, 82 # host_forwarder works.
59 'device_forwarder'), 83 self._device_process = pexpect.spawn(
60 Forwarder._DEVICE_FORWARDER_PATH) 84 'adb', ['-s',
61 self._host_forwarder_path = os.path.join(constants.CHROME_DIR, 85 self._adb._adb.GetSerialNumber(),
62 'out', 86 'shell',
63 build_type, 87 '%s %s -D --adb_sock=%s' % (
64 'host_forwarder') 88 tool.GetUtilWrapper(),
65 forward_string = ['%d:%d:%s' % 89 Forwarder._DEVICE_FORWARDER_PATH,
66 (device, host, host_name) for device, host in port_pairs] 90 Forwarder._DEVICE_ADB_CONTROL_PORT)])
67 logging.info('Forwarding ports: %s', forward_string) 91 device_success_re = re.compile('Starting Device Forwarder.')
68 timeout_sec = 5 92 device_failure_re = re.compile('.*:ERROR:(.*)')
69 host_pattern = 'host_forwarder.*' + ' '.join(forward_string) 93 index = self._device_process.expect([device_success_re,
70 # TODO(felipeg): Rather than using a blocking kill() here, the device 94 device_failure_re,
71 # forwarder could try to bind the Unix Domain Socket until it succeeds or 95 pexpect.EOF,
72 # while it fails because the socket is already bound (with appropriate 96 pexpect.TIMEOUT],
73 # timeout handling obviously). 97 Forwarder._TIMEOUT_SECS)
74 self._KillHostForwarderBlocking(host_pattern, timeout_sec) 98 if index == 1:
75 self._KillDeviceForwarderBlocking(timeout_sec) 99 error_msg = str(self._device_process.match.group(1))
76 self._adb_forward_process = pexpect.spawn( 100 logging.error(self._device_process.before)
77 'adb', ['-s', 101 self._device_process.close()
78 adb._adb.GetSerialNumber(), 102 raise Exception('Failed to start Device Forwarder with Error: %s' %
79 'forward', 103 error_msg)
80 'tcp:%s' % self._host_adb_control_port, 104 elif index == 2:
81 'localabstract:%s' % Forwarder._DEVICE_ADB_CONTROL_PORT]) 105 logging.error(self._device_process.before)
82 self._device_process = pexpect.spawn( 106 self._device_process.close()
83 'adb', ['-s', 107 raise Exception(
84 adb._adb.GetSerialNumber(), 108 'Unexpected EOF while trying to start Device Forwarder.')
85 'shell', 109 elif index == 3:
86 '%s %s -D --adb_sock=%s' % ( 110 logging.error(self._device_process.before)
87 tool.GetUtilWrapper(), 111 self._device_process.close()
88 Forwarder._DEVICE_FORWARDER_PATH, 112 raise Exception('Timeout while trying start Device Forwarder')
89 Forwarder._DEVICE_ADB_CONTROL_PORT)])
90 113
91 device_success_re = re.compile('Starting Device Forwarder.') 114 for redirection_command in redirection_commands:
92 device_failure_re = re.compile('.*:ERROR:(.*)') 115 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
93 index = self._device_process.expect([device_success_re, 116 [self._host_forwarder_path, redirection_command])
94 device_failure_re, 117 if exit_code != 0:
95 pexpect.EOF, 118 raise Exception('%s exited with %d: %s' % (self._host_forwarder_path,
96 pexpect.TIMEOUT], 119 exit_code, output))
97 Forwarder._TIMEOUT_SECS) 120 tokens = output.split(':')
98 if index == 1: 121 if len(tokens) != 2:
99 # Failure 122 raise Exception('Unexpected host forwarder output "%s", ' +
100 error_msg = str(self._device_process.match.group(1)) 123 'expected "device_port:host_port"' % output)
101 logging.error(self._device_process.before) 124 device_port = int(tokens[0])
102 self._CloseProcess() 125 host_port = int(tokens[1])
103 raise Exception('Failed to start Device Forwarder with Error: %s' % 126 self._host_to_device_port_map[host_port] = device_port
104 error_msg) 127 logging.info(
105 elif index == 2: 128 "Forwarding device port: %d to host port: %d." % (device_port,
106 logging.error(self._device_process.before) 129 host_port))
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 130
114 self._host_process = pexpect.spawn(self._host_forwarder_path, 131 def KillDeviceForwarder(self):
115 ['--adb_port=%s' % ( 132 logging.info('Killing device_forwarder.')
116 self._host_adb_control_port)] + 133 timeoutSec = 5
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( 134 processes_killed = self._adb.KillAllBlocking(
175 'device_forwarder', timeout_sec) 135 'device_forwarder', timeoutSec)
176 if not processes_killed: 136 if not processes_killed:
177 pids = self._adb.ExtractPid('device_forwarder') 137 pids = self._adb.ExtractPid('device_forwarder')
178 if pids: 138 if pids:
179 raise Exception('Timed out while killing device_forwarder') 139 raise Exception('Timed out while killing device_forwarder')
180 140
181 def _CloseProcess(self): 141 def KillHostForwarder(self):
182 if self._host_process: 142 (exit_code, output) = cmd_helper.GetCmdStatusAndOutput(
183 self._host_process.close() 143 [self._host_forwarder_path, 'kill-server'])
184 if self._device_process: 144 if exit_code != 0:
185 self._device_process.close() 145 raise Exception('%s exited with %d: %s' % (self._host_forwarder_path,
186 if self._adb_forward_process: 146 exit_code, output))
187 self._adb_forward_process.close()
188 self._host_process = None
189 self._device_process = None
190 self._adb_forward_process = None
191 147
192 def DevicePortForHostPort(self, host_port): 148 def DevicePortForHostPort(self, host_port):
193 """Get the device port that corresponds to a given host port.""" 149 """Get the device port that corresponds to a given host port."""
194 return self._host_to_device_port_map.get(host_port) 150 return self._host_to_device_port_map.get(host_port)
195 151
196 def Close(self): 152 def Close(self):
197 """Terminate the forwarder process.""" 153 """Terminate the forwarder process."""
198 self._CloseProcess() 154 if self._device_process:
155 self._device_process.close()
156 self._device_process = None
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698