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

Side by Side Diff: Tools/Scripts/webkitpy/layout_tests/port/chromium_android.py

Issue 18292002: Rename chromium-android port to android (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 7 years, 5 months 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
(Empty)
1 # Copyright (C) 2012 Google Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
5 # met:
6 #
7 # * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 # * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer
11 # in the documentation and/or other materials provided with the
12 # distribution.
13 # * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 import copy
30 import logging
31 import os
32 import re
33 import sys
34 import subprocess
35 import threading
36 import time
37
38 from webkitpy.layout_tests.port import chromium
39 from webkitpy.layout_tests.port import linux
40 from webkitpy.layout_tests.port import driver
41 from webkitpy.layout_tests.port import factory
42 from webkitpy.layout_tests.port import server_process
43 from webkitpy.common.system.profiler import SingleFileOutputProfiler
44
45 _log = logging.getLogger(__name__)
46
47 # The root directory for test resources, which has the same structure as the
48 # source root directory of Chromium.
49 # This path is defined in Chromium's base/test/test_support_android.cc.
50 DEVICE_SOURCE_ROOT_DIR = '/data/local/tmp/'
51
52 # The layout tests directory on device, which has two usages:
53 # 1. as a virtual path in file urls that will be bridged to HTTP.
54 # 2. pointing to some files that are pushed to the device for tests that
55 # don't work on file-over-http (e.g. blob protocol tests).
56 DEVICE_WEBKIT_BASE_DIR = DEVICE_SOURCE_ROOT_DIR + 'third_party/WebKit/'
57 DEVICE_LAYOUT_TESTS_DIR = DEVICE_WEBKIT_BASE_DIR + 'LayoutTests/'
58
59 SCALING_GOVERNORS_PATTERN = "/sys/devices/system/cpu/cpu*/cpufreq/scaling_govern or"
60 KPTR_RESTRICT_PATH = "/proc/sys/kernel/kptr_restrict"
61
62 # All the test cases are still served to the test runner through file protocol,
63 # but we use a file-to-http feature to bridge the file request to host's http
64 # server to get the real test files and corresponding resources.
65 # See webkit/support/platform_support_android.cc for the other side of this brid ge.
66 PERF_TEST_PATH_PREFIX = '/all-perf-tests'
67 LAYOUT_TEST_PATH_PREFIX = '/all-tests'
68
69 # All ports the Android forwarder to forward.
70 # 8000, 8080 and 8443 are for http/https tests.
71 # 8880 and 9323 are for websocket tests
72 # (see http_server.py, apache_http_server.py and websocket_server.py).
73 FORWARD_PORTS = '8000 8080 8443 8880 9323'
74
75 MS_TRUETYPE_FONTS_DIR = '/usr/share/fonts/truetype/msttcorefonts/'
76 MS_TRUETYPE_FONTS_PACKAGE = 'ttf-mscorefonts-installer'
77
78 # Timeout in seconds to wait for starting/stopping the driver.
79 DRIVER_START_STOP_TIMEOUT_SECS = 10
80
81 HOST_FONT_FILES = [
82 [[MS_TRUETYPE_FONTS_DIR], 'Arial.ttf', MS_TRUETYPE_FONTS_PACKAGE],
83 [[MS_TRUETYPE_FONTS_DIR], 'Arial_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
84 [[MS_TRUETYPE_FONTS_DIR], 'Arial_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE ],
85 [[MS_TRUETYPE_FONTS_DIR], 'Arial_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
86 [[MS_TRUETYPE_FONTS_DIR], 'Comic_Sans_MS.ttf', MS_TRUETYPE_FONTS_PACKAGE],
87 [[MS_TRUETYPE_FONTS_DIR], 'Comic_Sans_MS_Bold.ttf', MS_TRUETYPE_FONTS_PACKAG E],
88 [[MS_TRUETYPE_FONTS_DIR], 'Courier_New.ttf', MS_TRUETYPE_FONTS_PACKAGE],
89 [[MS_TRUETYPE_FONTS_DIR], 'Courier_New_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE] ,
90 [[MS_TRUETYPE_FONTS_DIR], 'Courier_New_Bold_Italic.ttf', MS_TRUETYPE_FONTS_P ACKAGE],
91 [[MS_TRUETYPE_FONTS_DIR], 'Courier_New_Italic.ttf', MS_TRUETYPE_FONTS_PACKAG E],
92 [[MS_TRUETYPE_FONTS_DIR], 'Georgia.ttf', MS_TRUETYPE_FONTS_PACKAGE],
93 [[MS_TRUETYPE_FONTS_DIR], 'Georgia_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
94 [[MS_TRUETYPE_FONTS_DIR], 'Georgia_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKA GE],
95 [[MS_TRUETYPE_FONTS_DIR], 'Georgia_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
96 [[MS_TRUETYPE_FONTS_DIR], 'Impact.ttf', MS_TRUETYPE_FONTS_PACKAGE],
97 [[MS_TRUETYPE_FONTS_DIR], 'Trebuchet_MS.ttf', MS_TRUETYPE_FONTS_PACKAGE],
98 [[MS_TRUETYPE_FONTS_DIR], 'Trebuchet_MS_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE ],
99 [[MS_TRUETYPE_FONTS_DIR], 'Trebuchet_MS_Bold_Italic.ttf', MS_TRUETYPE_FONTS_ PACKAGE],
100 [[MS_TRUETYPE_FONTS_DIR], 'Trebuchet_MS_Italic.ttf', MS_TRUETYPE_FONTS_PACKA GE],
101 [[MS_TRUETYPE_FONTS_DIR], 'Times_New_Roman.ttf', MS_TRUETYPE_FONTS_PACKAGE],
102 [[MS_TRUETYPE_FONTS_DIR], 'Times_New_Roman_Bold.ttf', MS_TRUETYPE_FONTS_PACK AGE],
103 [[MS_TRUETYPE_FONTS_DIR], 'Times_New_Roman_Bold_Italic.ttf', MS_TRUETYPE_FON TS_PACKAGE],
104 [[MS_TRUETYPE_FONTS_DIR], 'Times_New_Roman_Italic.ttf', MS_TRUETYPE_FONTS_PA CKAGE],
105 [[MS_TRUETYPE_FONTS_DIR], 'Verdana.ttf', MS_TRUETYPE_FONTS_PACKAGE],
106 [[MS_TRUETYPE_FONTS_DIR], 'Verdana_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
107 [[MS_TRUETYPE_FONTS_DIR], 'Verdana_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKA GE],
108 [[MS_TRUETYPE_FONTS_DIR], 'Verdana_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
109 # The Microsoft font EULA
110 [['/usr/share/doc/ttf-mscorefonts-installer/'], 'READ_ME!.gz', MS_TRUETYPE_F ONTS_PACKAGE],
111 # Other fonts: Arabic, CJK, Indic, Thai, etc.
112 [['/usr/share/fonts/truetype/ttf-dejavu/'], 'DejaVuSans.ttf', 'ttf-dejavu'],
113 [['/usr/share/fonts/truetype/kochi/'], 'kochi-mincho.ttf', 'ttf-kochi-mincho '],
114 [['/usr/share/fonts/truetype/ttf-indic-fonts-core/'], 'lohit_hi.ttf', 'ttf-i ndic-fonts-core'],
115 [['/usr/share/fonts/truetype/ttf-indic-fonts-core/'], 'lohit_ta.ttf', 'ttf-i ndic-fonts-core'],
116 [['/usr/share/fonts/truetype/ttf-indic-fonts-core/'], 'MuktiNarrow.ttf', 'tt f-indic-fonts-core'],
117 [['/usr/share/fonts/truetype/thai/', '/usr/share/fonts/truetype/tlwg/'], 'Ga ruda.ttf', 'fonts-tlwg-garuda'],
118 [['/usr/share/fonts/truetype/ttf-indic-fonts-core/', '/usr/share/fonts/truet ype/ttf-punjabi-fonts/'], 'lohit_pa.ttf', 'ttf-indic-fonts-core'],
119 ]
120
121 # Test resources that need to be accessed as files directly.
122 # Each item can be the relative path of a directory or a file.
123 TEST_RESOURCES_TO_PUSH = [
124 # Blob tests need to access files directly.
125 'editing/pasteboard/resources',
126 'fast/files/resources',
127 'http/tests/local/resources',
128 'http/tests/local/formdata/resources',
129 # User style URLs are accessed as local files in webkit_support.
130 'http/tests/security/resources/cssStyle.css',
131 # Media tests need to access audio/video as files.
132 'media/content',
133 'compositing/resources/video.mp4',
134 ]
135
136 # List of test resources from webkit platform. These resources will be copied to the external storage.
137 WEBKIT_PLATFORM_RESOURCES_TO_PUSH = [
138 'third_party/hyphen/hyph_en_US.dic',
139 ]
140
141 MD5SUM_DEVICE_FILE_NAME = 'md5sum_bin'
142 MD5SUM_HOST_FILE_NAME = 'md5sum_bin_host'
143 MD5SUM_DEVICE_PATH = '/data/local/tmp/' + MD5SUM_DEVICE_FILE_NAME
144
145
146 # Information required when running layout tests using content_shell as the test runner.
147 class ContentShellDriverDetails():
148 def device_cache_directory(self):
149 return self.device_directory() + 'cache/'
150
151 def device_fonts_directory(self):
152 return self.device_directory() + 'fonts/'
153
154 def device_forwarder_path(self):
155 return self.device_directory() + 'forwarder'
156
157 def device_fifo_directory(self):
158 return '/data/data/' + self.package_name() + '/files/'
159
160 def apk_name(self):
161 return 'apks/ContentShell.apk'
162
163 def package_name(self):
164 return 'org.chromium.content_shell_apk'
165
166 def activity_name(self):
167 return self.package_name() + '/.ContentShellActivity'
168
169 def library_name(self):
170 return 'libcontent_shell_content_view.so'
171
172 def additional_resources(self):
173 return ['content_resources.pak', 'shell_resources.pak']
174
175 def command_line_file(self):
176 return '/data/local/tmp/content-shell-command-line'
177
178 def additional_command_line_flags(self):
179 return ['--dump-render-tree', '--encode-binary']
180
181 def device_directory(self):
182 return DEVICE_SOURCE_ROOT_DIR + 'content_shell/'
183
184
185 # The AndroidCommands class encapsulates commands to communicate with an attache d device.
186 class AndroidCommands(object):
187 _adb_command_path = None
188 _adb_command_path_options = []
189
190 def __init__(self, executive, device_serial):
191 self._executive = executive
192 self._device_serial = device_serial
193
194 # Local public methods.
195
196 def file_exists(self, full_path):
197 assert full_path.startswith('/')
198 return self.run(['shell', 'ls', full_path]).strip() == full_path
199
200 def push(self, host_path, device_path, ignore_error=False):
201 return self.run(['push', host_path, device_path], ignore_error=ignore_er ror)
202
203 def pull(self, device_path, host_path, ignore_error=False):
204 return self.run(['pull', device_path, host_path], ignore_error=ignore_er ror)
205
206 def mkdir(self, device_path, chmod=None):
207 self.run(['shell', 'mkdir', '-p', device_path])
208 if chmod:
209 self.run(['shell', 'chmod', chmod, device_path])
210
211 def restart_as_root(self):
212 output = self.run(['root'])
213 if 'adbd is already running as root' in output:
214 return
215
216 elif not 'restarting adbd as root' in output:
217 self._log_error('Unrecognized output from adb root: %s' % output)
218
219 self.run(['wait-for-device'])
220
221 def run(self, command, ignore_error=False):
222 self._log_debug('Run adb command: ' + str(command))
223 if ignore_error:
224 error_handler = self._executive.ignore_error
225 else:
226 error_handler = None
227
228 result = self._executive.run_command(self.adb_command() + command,
229 error_handler=error_handler)
230
231 # We limit the length to avoid outputting too verbose commands, such as "adb logcat".
232 self._log_debug('Run adb result: ' + result[:80])
233 return result
234
235 def get_serial(self):
236 return self._device_serial
237
238 def adb_command(self):
239 return [AndroidCommands.adb_command_path(self._executive), '-s', self._d evice_serial]
240
241 @staticmethod
242 def set_adb_command_path_options(paths):
243 AndroidCommands._adb_command_path_options = paths
244
245 @staticmethod
246 def adb_command_path(executive):
247 if AndroidCommands._adb_command_path:
248 return AndroidCommands._adb_command_path
249
250 assert AndroidCommands._adb_command_path_options, 'No commands paths hav e been set to look for the "adb" command.'
251
252 command_path = None
253 command_version = None
254 for path_option in AndroidCommands._adb_command_path_options:
255 path_version = AndroidCommands._determine_adb_version(path_option, e xecutive)
256 if not path_version:
257 continue
258 if command_version != None and path_version < command_version:
259 continue
260
261 command_path = path_option
262 command_version = path_version
263
264 assert command_path, 'Unable to locate the "adb" command. Are you using an Android checkout of Chromium?'
265
266 AndroidCommands._adb_command_path = command_path
267 return command_path
268
269 # Local private methods.
270
271 def _log_error(self, message):
272 _log.error('[%s] %s' % (self._device_serial, message))
273
274 def _log_debug(self, message):
275 _log.debug('[%s] %s' % (self._device_serial, message))
276
277 @staticmethod
278 def _determine_adb_version(adb_command_path, executive):
279 re_version = re.compile('^.*version ([\d\.]+)$')
280 try:
281 output = executive.run_command([adb_command_path, 'version'], error_ handler=executive.ignore_error)
282 except OSError:
283 return None
284
285 result = re_version.match(output)
286 if not output or not result:
287 return None
288
289 return [int(n) for n in result.group(1).split('.')]
290
291
292 # A class to encapsulate device status and information, such as the AndroidComma nds
293 # instances and whether the device has been set up.
294 class AndroidDevices(object):
295 # Percentage of battery a device needs to have in order for it to be conside red
296 # to participate in running the layout tests.
297 MINIMUM_BATTERY_PERCENTAGE = 30
298
299 def __init__(self, executive, default_device=None):
300 self._usable_devices = []
301 self._default_device = default_device
302 self._prepared_devices = []
303
304 def usable_devices(self, executive):
305 if self._usable_devices:
306 return self._usable_devices
307
308 if self._default_device:
309 self._usable_devices = [AndroidCommands(executive, self._default_dev ice)]
310 return self._usable_devices
311
312 # Example "adb devices" command output:
313 # List of devices attached
314 # 0123456789ABCDEF device
315 re_device = re.compile('^([a-zA-Z0-9_:.-]+)\tdevice$', re.MULTILINE)
316
317 result = executive.run_command([AndroidCommands.adb_command_path(executi ve), 'devices'],
318 error_handler=executive.ignore_error)
319 devices = re_device.findall(result)
320 if not devices:
321 raise AssertionError('Unable to find attached Android devices. ADB o utput: %s' % result)
322
323 for device_serial in devices:
324 commands = AndroidCommands(executive, device_serial)
325 if self._battery_level_for_device(commands) < AndroidDevices.MINIMUM _BATTERY_PERCENTAGE:
326 continue
327
328 self._usable_devices.append(commands)
329
330 if not self._usable_devices:
331 raise AssertionError('No devices attached with more than %d percent battery.' %
332 AndroidDevices.MINIMUM_BATTERY_PERCENTAGE)
333
334 return self._usable_devices
335
336 def get_device(self, executive, device_index):
337 devices = self.usable_devices(executive)
338 if device_index >= len(devices):
339 raise AssertionError('Device index exceeds number of usable devices. ')
340
341 return devices[device_index]
342
343 def is_device_prepared(self, device_serial):
344 return device_serial in self._prepared_devices
345
346 def set_device_prepared(self, device_serial):
347 self._prepared_devices.append(device_serial)
348
349 # Private methods
350 def _battery_level_for_device(self, commands):
351 battery_status = commands.run(['shell', 'dumpsys', 'battery'])
352 if 'Error' in battery_status:
353 _log.warning('Unable to read the battery level from device with seri al "%s".' % commands.get_serial())
354 return 100
355
356 return int(re.findall('level: (\d+)', battery_status)[0])
357
358
359 class ChromiumAndroidPort(chromium.ChromiumPort):
360 port_name = 'chromium-android'
361
362 # Avoid initializing the adb path [worker count]+1 times by storing it as a static member.
363 _adb_path = None
364
365 SUPPORTED_VERSIONS = ('android')
366
367 FALLBACK_PATHS = { 'android': [ 'chromium-android' ] + linux.LinuxPort.lates t_platform_fallback_path() }
368
369 def __init__(self, host, port_name, **kwargs):
370 super(ChromiumAndroidPort, self).__init__(host, port_name, **kwargs)
371
372 self._operating_system = 'android'
373 self._version = 'icecreamsandwich'
374
375 self._host_port = factory.PortFactory(host).get('chromium', **kwargs)
376 self._server_process_constructor = self._android_server_process_construc tor
377
378 if self.driver_name() != self.CONTENT_SHELL_NAME:
379 raise AssertionError('Layout tests on Android only support content_s hell as the driver.')
380
381 self._driver_details = ContentShellDriverDetails()
382
383 # Initialize the AndroidDevices class which tracks available devices.
384 default_device = None
385 if hasattr(self._options, 'adb_device') and len(self._options.adb_device ):
386 default_device = self._options.adb_device
387
388 self._devices = AndroidDevices(self._executive, default_device)
389
390 # Tell AndroidCommands where to search for the "adb" command.
391 AndroidCommands.set_adb_command_path_options(['adb',
392 self.path_from_chromium_base('third_party', 'android_tools', 'sdk', 'platform-tools', 'adb')])
393
394 # Local public methods.
395 def path_to_forwarder(self):
396 return self._build_path('forwarder')
397
398 def path_to_md5sum(self):
399 return self._build_path(MD5SUM_DEVICE_FILE_NAME)
400
401 def path_to_md5sum_host(self):
402 return self._build_path(MD5SUM_HOST_FILE_NAME)
403
404 # Overridden public methods.
405 def buildbot_archives_baselines(self):
406 return False
407
408 def additional_drt_flag(self):
409 return self._driver_details.additional_command_line_flags()
410
411 def default_timeout_ms(self):
412 # Android platform has less computing power than desktop platforms.
413 # Using 10 seconds allows us to pass most slow tests which are not
414 # marked as slow tests on desktop platforms.
415 return 10 * 1000
416
417 def driver_stop_timeout(self):
418 # The driver doesn't respond to closing stdin, so we might as well stop the driver immediately.
419 return 0.0
420
421 def default_child_processes(self):
422 usable_device_count = len(self._devices.usable_devices(self._executive))
423 if not usable_device_count:
424 raise AssertionError('There are no devices available to run the layo ut tests on.')
425
426 return usable_device_count
427
428 def default_baseline_search_path(self):
429 return map(self._webkit_baseline_path, self.FALLBACK_PATHS['android'])
430
431 def check_wdiff(self, logging=True):
432 return self._host_port.check_wdiff(logging)
433
434 def check_build(self, needs_http):
435 result = super(ChromiumAndroidPort, self).check_build(needs_http)
436 result = self._check_file_exists(self.path_to_md5sum(), 'md5sum utility' ) and result
437 result = self._check_file_exists(self.path_to_md5sum_host(), 'md5sum hos t utility') and result
438 result = self._check_file_exists(self.path_to_forwarder(), 'forwarder ut ility') and result
439 if not result:
440 _log.error('For complete Android build requirements, please see:')
441 _log.error('')
442 _log.error(' http://code.google.com/p/chromium/wiki/AndroidBuildI nstructions')
443
444 return result
445
446 def check_sys_deps(self, needs_http):
447 for (font_dirs, font_file, package) in HOST_FONT_FILES:
448 exists = False
449 for font_dir in font_dirs:
450 font_path = font_dir + font_file
451 if self._check_file_exists(font_path, '', logging=False):
452 exists = True
453 break
454 if not exists:
455 _log.error('You are missing %s under %s. Try installing %s. See build instructions.' % (font_file, font_dirs, package))
456 return False
457 return True
458
459 def requires_http_server(self):
460 """Chromium Android runs tests on devices, and uses the HTTP server to
461 serve the actual layout tests to the test driver."""
462 return True
463
464 def start_http_server(self, additional_dirs=None, number_of_servers=0):
465 if not additional_dirs:
466 additional_dirs = {}
467 additional_dirs[PERF_TEST_PATH_PREFIX] = self.perf_tests_dir()
468 additional_dirs[LAYOUT_TEST_PATH_PREFIX] = self.layout_tests_dir()
469 super(ChromiumAndroidPort, self).start_http_server(additional_dirs, numb er_of_servers)
470
471 def create_driver(self, worker_number, no_timeout=False):
472 return ChromiumAndroidDriver(self, worker_number, pixel_tests=self.get_o ption('pixel_tests'),
473 driver_details=self._driver_details,
474 android_devices=self._devices,
475 # Force no timeout to avoid test driver tim eouts before NRWT.
476 no_timeout=True)
477
478 def driver_cmd_line(self):
479 # Override to return the actual test driver's command line.
480 return self.create_driver(0)._android_driver_cmd_line(self.get_option('p ixel_tests'), [])
481
482 # Overridden protected methods.
483
484 def _build_path(self, *comps):
485 return self._host_port._build_path(*comps)
486
487 def _build_path_with_configuration(self, configuration, *comps):
488 return self._host_port._build_path_with_configuration(configuration, *co mps)
489
490 def _path_to_apache(self):
491 return self._host_port._path_to_apache()
492
493 def _path_to_apache_config_file(self):
494 return self._host_port._path_to_apache_config_file()
495
496 def _path_to_driver(self, configuration=None):
497 return self._build_path_with_configuration(configuration, self._driver_d etails.apk_name())
498
499 def _path_to_helper(self):
500 return None
501
502 def _path_to_image_diff(self):
503 return self._host_port._path_to_image_diff()
504
505 def _path_to_lighttpd(self):
506 return self._host_port._path_to_lighttpd()
507
508 def _path_to_lighttpd_modules(self):
509 return self._host_port._path_to_lighttpd_modules()
510
511 def _path_to_lighttpd_php(self):
512 return self._host_port._path_to_lighttpd_php()
513
514 def _path_to_wdiff(self):
515 return self._host_port._path_to_wdiff()
516
517 def _shut_down_http_server(self, pid):
518 return self._host_port._shut_down_http_server(pid)
519
520 def _driver_class(self):
521 return ChromiumAndroidDriver
522
523 # Local private methods.
524
525 @staticmethod
526 def _android_server_process_constructor(port, server_name, cmd_line, env=Non e):
527 return server_process.ServerProcess(port, server_name, cmd_line, env,
528 universal_newlines=True, treat_no_da ta_as_crash=True)
529
530
531 class AndroidPerf(SingleFileOutputProfiler):
532 _cached_perf_host_path = None
533 _have_searched_for_perf_host = False
534
535 def __init__(self, host, executable_path, output_dir, android_commands, symf s_path, kallsyms_path, identifier=None):
536 super(AndroidPerf, self).__init__(host, executable_path, output_dir, "da ta", identifier)
537 self._android_commands = android_commands
538 self._perf_process = None
539 self._symfs_path = symfs_path
540 self._kallsyms_path = kallsyms_path
541
542 def check_configuration(self):
543 # Check that perf is installed
544 if not self._android_commands.file_exists('/system/bin/perf'):
545 print "Cannot find /system/bin/perf on device %s" % self._android_co mmands.get_serial()
546 return False
547
548 # Check that the device is a userdebug build (or at least has the necess ary libraries).
549 if self._android_commands.run(['shell', 'getprop', 'ro.build.type']).str ip() != 'userdebug':
550 print "Device %s is not flashed with a userdebug build of Android" % self._android_commands.get_serial()
551 return False
552
553 # FIXME: Check that the binary actually is perf-able (has stackframe poi nters)?
554 # objdump -s a function and make sure it modifies the fp?
555 # Instruct users to rebuild after export GYP_DEFINES="profiling=1 $GYP_D EFINES"
556 return True
557
558 def print_setup_instructions(self):
559 print """
560 perf on android requires a 'userdebug' build of Android, see:
561 http://source.android.com/source/building-devices.html"
562
563 The perf command can be built from:
564 https://android.googlesource.com/platform/external/linux-tools-perf/
565 and requires libefl, libebl, libdw, and libdwfl available in:
566 https://android.googlesource.com/platform/external/elfutils/
567
568 The test driver must be built with profiling=1, make sure you've done:
569 export GYP_DEFINES="profiling=1 $GYP_DEFINES"
570 update-webkit --chromium-android
571 build-webkit --chromium-android
572
573 Googlers should read:
574 http://goto.google.com/cr-android-perf-howto
575 """
576
577 def attach_to_pid(self, pid):
578 assert(pid)
579 assert(self._perf_process == None)
580 # FIXME: This can't be a fixed timeout!
581 cmd = self._android_commands.adb_command() + ['shell', 'perf', 'record', '-g', '-p', pid, 'sleep', 30]
582 self._perf_process = self._host.executive.popen(cmd)
583
584 def _perf_version_string(self, perf_path):
585 try:
586 return self._host.executive.run_command([perf_path, '--version'])
587 except:
588 return None
589
590 def _find_perfhost_binary(self):
591 perfhost_version = self._perf_version_string('perfhost_linux')
592 if perfhost_version:
593 return 'perfhost_linux'
594 perf_version = self._perf_version_string('perf')
595 if perf_version:
596 return 'perf'
597 return None
598
599 def _perfhost_path(self):
600 if self._have_searched_for_perf_host:
601 return self._cached_perf_host_path
602 self._have_searched_for_perf_host = True
603 self._cached_perf_host_path = self._find_perfhost_binary()
604 return self._cached_perf_host_path
605
606 def _first_ten_lines_of_profile(self, perf_output):
607 match = re.search("^#[^\n]*\n((?: [^\n]*\n){1,10})", perf_output, re.MUL TILINE)
608 return match.group(1) if match else None
609
610 def profile_after_exit(self):
611 perf_exitcode = self._perf_process.wait()
612 if perf_exitcode != 0:
613 print "Perf failed (exit code: %i), can't process results." % perf_e xitcode
614 return
615
616 self._android_commands.pull('/data/perf.data', self._output_path)
617
618 perfhost_path = self._perfhost_path()
619 perfhost_report_command = [
620 'report',
621 '--input', self._output_path,
622 '--symfs', self._symfs_path,
623 '--kallsyms', self._kallsyms_path,
624 ]
625 if perfhost_path:
626 perfhost_args = [perfhost_path] + perfhost_report_command + ['--call -graph', 'none']
627 perf_output = self._host.executive.run_command(perfhost_args)
628 # We could save off the full -g report to a file if users found that useful.
629 print self._first_ten_lines_of_profile(perf_output)
630 else:
631 print """
632 Failed to find perfhost_linux binary, can't process samples from the device.
633
634 perfhost_linux can be built from:
635 https://android.googlesource.com/platform/external/linux-tools-perf/
636 also, modern versions of perf (available from apt-get install goobuntu-kernel-to ols-common)
637 may also be able to process the perf.data files from the device.
638
639 Googlers should read:
640 http://goto.google.com/cr-android-perf-howto
641 for instructions on installing pre-built copies of perfhost_linux
642 http://crbug.com/165250 discusses making these pre-built binaries externally ava ilable.
643 """
644
645 perfhost_display_patch = perfhost_path if perfhost_path else 'perfhost_l inux'
646 print "To view the full profile, run:"
647 print ' '.join([perfhost_display_patch] + perfhost_report_command)
648
649
650 class ChromiumAndroidDriver(driver.Driver):
651 def __init__(self, port, worker_number, pixel_tests, driver_details, android _devices, no_timeout=False):
652 super(ChromiumAndroidDriver, self).__init__(port, worker_number, pixel_t ests, no_timeout)
653 self._in_fifo_path = driver_details.device_fifo_directory() + 'stdin.fif o'
654 self._out_fifo_path = driver_details.device_fifo_directory() + 'test.fif o'
655 self._err_fifo_path = driver_details.device_fifo_directory() + 'stderr.f ifo'
656 self._read_stdout_process = None
657 self._read_stderr_process = None
658 self._forwarder_process = None
659 self._original_governors = {}
660 self._original_kptr_restrict = None
661
662 self._android_devices = android_devices
663 self._android_commands = android_devices.get_device(port._executive, wor ker_number)
664 self._driver_details = driver_details
665
666 # FIXME: If we taught ProfileFactory about "target" devices we could
667 # just use the logic in Driver instead of duplicating it here.
668 if self._port.get_option("profile"):
669 # FIXME: This should be done once, instead of per-driver!
670 symfs_path = self._find_or_create_symfs()
671 kallsyms_path = self._update_kallsyms_cache(symfs_path)
672 # FIXME: We should pass this some sort of "Bridge" object abstractio n around ADB instead of a path/device pair.
673 self._profiler = AndroidPerf(self._port.host, self._port._path_to_dr iver(), self._port.results_directory(),
674 self._android_commands, symfs_path, kallsyms_path)
675 # FIXME: This is a layering violation and should be moved to Port.ch eck_sys_deps
676 # once we have an abstraction around an adb_path/device_serial pair to make it
677 # easy to make these class methods on AndroidPerf.
678 if not self._profiler.check_configuration():
679 self._profiler.print_setup_instructions()
680 sys.exit(1)
681 else:
682 self._profiler = None
683
684 def __del__(self):
685 self._teardown_performance()
686 super(ChromiumAndroidDriver, self).__del__()
687
688 def _update_kallsyms_cache(self, output_dir):
689 kallsyms_name = "%s-kallsyms" % self._android_commands.get_serial()
690 kallsyms_cache_path = self._port.host.filesystem.join(output_dir, kallsy ms_name)
691
692 self._android_commands.restart_as_root()
693
694 saved_kptr_restrict = self._android_commands.run(['shell', 'cat', KPTR_R ESTRICT_PATH]).strip()
695 self._android_commands.run(['shell', 'echo', '0', '>', KPTR_RESTRICT_PAT H])
696
697 print "Updating kallsyms file (%s) from device" % kallsyms_cache_path
698 self._android_commands.pull("/proc/kallsyms", kallsyms_cache_path)
699
700 self._android_commands.run(['shell', 'echo', saved_kptr_restrict, '>', K PTR_RESTRICT_PATH])
701
702 return kallsyms_cache_path
703
704 def _find_or_create_symfs(self):
705 environment = self._port.host.copy_current_environment()
706 env = environment.to_dictionary()
707 fs = self._port.host.filesystem
708
709 if 'ANDROID_SYMFS' in env:
710 symfs_path = env['ANDROID_SYMFS']
711 else:
712 symfs_path = fs.join(self._port.results_directory(), 'symfs')
713 print "ANDROID_SYMFS not set, using %s" % symfs_path
714
715 # find the installed path, and the path of the symboled built library
716 # FIXME: We should get the install path from the device!
717 symfs_library_path = fs.join(symfs_path, "data/app-lib/%s-1/%s" % (self. _driver_details.package_name(), self._driver_details.library_name()))
718 built_library_path = self._port._build_path('lib', self._driver_details. library_name())
719 assert(fs.exists(built_library_path))
720
721 # FIXME: Ideally we'd check the sha1's first and make a soft-link instea d of copying (since we probably never care about windows).
722 print "Updating symfs libary (%s) from built copy (%s)" % (symfs_library _path, built_library_path)
723 fs.maybe_make_directory(fs.dirname(symfs_library_path))
724 fs.copyfile(built_library_path, symfs_library_path)
725
726 return symfs_path
727
728 def _setup_md5sum_and_push_data_if_needed(self):
729 self._md5sum_path = self._port.path_to_md5sum()
730 if not self._android_commands.file_exists(MD5SUM_DEVICE_PATH):
731 if not self._android_commands.push(self._md5sum_path, MD5SUM_DEVICE_ PATH):
732 raise AssertionError('Could not push md5sum to device')
733
734 self._push_executable()
735 self._push_fonts()
736 self._push_test_resources()
737 self._push_platform_resources()
738
739 def _setup_test(self):
740 if self._android_devices.is_device_prepared(self._android_commands.get_s erial()):
741 return
742
743 self._android_commands.restart_as_root()
744 self._setup_md5sum_and_push_data_if_needed()
745 self._setup_performance()
746
747 # Required by webkit_support::GetWebKitRootDirFilePath().
748 # Other directories will be created automatically by adb push.
749 self._android_commands.mkdir(DEVICE_SOURCE_ROOT_DIR + 'chrome')
750
751 # Allow the test driver to get full read and write access to the directo ry on the device,
752 # as well as for the FIFOs. We'll need a world writable directory.
753 self._android_commands.mkdir(self._driver_details.device_directory(), ch mod='777')
754 self._android_commands.mkdir(self._driver_details.device_fifo_directory( ), chmod='777')
755
756 # Make sure that the disk cache on the device resets to a clean state.
757 self._android_commands.run(['shell', 'rm', '-r', self._driver_details.de vice_cache_directory()])
758
759 # Mark this device as having been set up.
760 self._android_devices.set_device_prepared(self._android_commands.get_ser ial())
761
762 def _log_error(self, message):
763 _log.error('[%s] %s' % (self._android_commands.get_serial(), message))
764
765 def _log_debug(self, message):
766 _log.debug('[%s] %s' % (self._android_commands.get_serial(), message))
767
768 def _abort(self, message):
769 raise AssertionError('[%s] %s' % (self._android_commands.get_serial(), m essage))
770
771 @staticmethod
772 def _extract_hashes_from_md5sum_output(md5sum_output):
773 assert md5sum_output
774 return [line.split(' ')[0] for line in md5sum_output]
775
776 def _push_file_if_needed(self, host_file, device_file):
777 assert os.path.exists(host_file)
778 device_hashes = self._extract_hashes_from_md5sum_output(
779 self._port.host.executive.popen(self._android_commands.adb_comma nd() + ['shell', MD5SUM_DEVICE_PATH, device_file],
780 stdout=subprocess.PIPE).stdout)
781 host_hashes = self._extract_hashes_from_md5sum_output(
782 self._port.host.executive.popen(args=['%s_host' % self._md5sum_p ath, host_file],
783 stdout=subprocess.PIPE).stdout)
784 if host_hashes and device_hashes == host_hashes:
785 return
786
787 self._android_commands.push(host_file, device_file)
788
789 def _push_executable(self):
790 self._push_file_if_needed(self._port.path_to_forwarder(), self._driver_d etails.device_forwarder_path())
791 for resource in self._driver_details.additional_resources():
792 self._push_file_if_needed(self._port._build_path(resource), self._dr iver_details.device_directory() + resource)
793
794 self._push_file_if_needed(self._port._build_path('android_main_fonts.xml '), self._driver_details.device_directory() + 'android_main_fonts.xml')
795 self._push_file_if_needed(self._port._build_path('android_fallback_fonts .xml'), self._driver_details.device_directory() + 'android_fallback_fonts.xml')
796
797 self._android_commands.run(['uninstall', self._driver_details.package_na me()])
798 driver_host_path = self._port._path_to_driver()
799 install_result = self._android_commands.run(['install', driver_host_path ])
800 if install_result.find('Success') == -1:
801 self._abort('Failed to install %s onto device: %s' % (driver_host_pa th, install_result))
802
803 def _push_fonts(self):
804 self._log_debug('Pushing fonts')
805 path_to_ahem_font = self._port._build_path('AHEM____.TTF')
806 self._push_file_if_needed(path_to_ahem_font, self._driver_details.device _fonts_directory() + 'AHEM____.TTF')
807 for (host_dirs, font_file, package) in HOST_FONT_FILES:
808 for host_dir in host_dirs:
809 host_font_path = host_dir + font_file
810 if self._port._check_file_exists(host_font_path, '', logging=Fal se):
811 self._push_file_if_needed(host_font_path, self._driver_detai ls.device_fonts_directory() + font_file)
812
813 def _push_test_resources(self):
814 self._log_debug('Pushing test resources')
815 for resource in TEST_RESOURCES_TO_PUSH:
816 self._push_file_if_needed(self._port.layout_tests_dir() + '/' + reso urce, DEVICE_LAYOUT_TESTS_DIR + resource)
817
818 def _push_platform_resources(self):
819 self._log_debug('Pushing platform resources')
820 external_storage = self._port._filesystem.join(self._android_commands.ru n(['shell', 'echo $EXTERNAL_STORAGE']).strip(), 'Source', 'WebKit', 'chromium')
821 for resource in WEBKIT_PLATFORM_RESOURCES_TO_PUSH:
822 self._push_file_if_needed(self._port._chromium_base_dir(self._port._ filesystem) + '/' + resource, external_storage + '/' + resource)
823
824 def _get_last_stacktrace(self):
825 tombstones = self._android_commands.run(['shell', 'ls', '-n', '/data/tom bstones'])
826 if not tombstones or tombstones.startswith('/data/tombstones: No such fi le or directory'):
827 self._log_error('The driver crashed, but no tombstone found!')
828 return ''
829 tombstones = tombstones.rstrip().split('\n')
830 last_tombstone = tombstones[0].split()
831 for tombstone in tombstones[1:]:
832 # Format of fields:
833 # 0 1 2 3 4 5 6
834 # permission uid gid size date time filename
835 # -rw------- 1000 1000 45859 2011-04-13 06:00 tombstone_00
836 fields = tombstone.split()
837 if (fields[4] + fields[5] >= last_tombstone[4] + last_tombstone[5]):
838 last_tombstone = fields
839 else:
840 break
841
842 # Use Android tool vendor/google/tools/stack to convert the raw
843 # stack trace into a human readable format, if needed.
844 # It takes a long time, so don't do it here.
845 return '%s\n%s' % (' '.join(last_tombstone),
846 self._android_commands.run(['shell', 'cat', '/data/to mbstones/' + last_tombstone[6]]))
847
848 def _get_logcat(self):
849 return self._android_commands.run(['logcat', '-d', '-v', 'threadtime'])
850
851 def _setup_performance(self):
852 # Disable CPU scaling and drop ram cache to reduce noise in tests
853 if not self._original_governors:
854 governor_files = self._android_commands.run(['shell', 'ls', SCALING_ GOVERNORS_PATTERN])
855 if governor_files.find('No such file or directory') == -1:
856 for file in governor_files.split():
857 self._original_governors[file] = self._android_commands.run( ['shell', 'cat', file]).strip()
858 self._android_commands.run(['shell', 'echo', 'performance', '>', file])
859
860 def _teardown_performance(self):
861 for file, original_content in self._original_governors.items():
862 self._android_commands.run(['shell', 'echo', original_content, '>', file])
863 self._original_governors = {}
864
865 def _get_crash_log(self, stdout, stderr, newer_than):
866 if not stdout:
867 stdout = ''
868 stdout += '********* [%s] Logcat:\n%s' % (self._android_commands.get_ser ial(), self._get_logcat())
869 if not stderr:
870 stderr = ''
871 stderr += '********* [%s] Tombstone file:\n%s' % (self._android_commands .get_serial(), self._get_last_stacktrace())
872 return super(ChromiumAndroidDriver, self)._get_crash_log(stdout, stderr, newer_than)
873
874 def cmd_line(self, pixel_tests, per_test_args):
875 # The returned command line is used to start _server_process. In our cas e, it's an interactive 'adb shell'.
876 # The command line passed to the driver process is returned by _driver_c md_line() instead.
877 return self._android_commands.adb_command() + ['shell']
878
879 def _android_driver_cmd_line(self, pixel_tests, per_test_args):
880 return driver.Driver.cmd_line(self, pixel_tests, per_test_args)
881
882 @staticmethod
883 def _loop_with_timeout(condition, timeout_secs):
884 deadline = time.time() + timeout_secs
885 while time.time() < deadline:
886 if condition():
887 return True
888 return False
889
890 def _all_pipes_created(self):
891 return (self._android_commands.file_exists(self._in_fifo_path) and
892 self._android_commands.file_exists(self._out_fifo_path) and
893 self._android_commands.file_exists(self._err_fifo_path))
894
895 def _remove_all_pipes(self):
896 for file in [self._in_fifo_path, self._out_fifo_path, self._err_fifo_pat h]:
897 self._android_commands.run(['shell', 'rm', file])
898
899 return (not self._android_commands.file_exists(self._in_fifo_path) and
900 not self._android_commands.file_exists(self._out_fifo_path) and
901 not self._android_commands.file_exists(self._err_fifo_path))
902
903 def run_test(self, driver_input, stop_when_done):
904 base = self._port.lookup_virtual_test_base(driver_input.test_name)
905 if base:
906 driver_input = copy.copy(driver_input)
907 driver_input.args = self._port.lookup_virtual_test_args(driver_input .test_name)
908 driver_input.test_name = base
909 return super(ChromiumAndroidDriver, self).run_test(driver_input, stop_wh en_done)
910
911 def start(self, pixel_tests, per_test_args):
912 # We override the default start() so that we can call _android_driver_cm d_line()
913 # instead of cmd_line().
914 new_cmd_line = self._android_driver_cmd_line(pixel_tests, per_test_args)
915
916 # Since _android_driver_cmd_line() is different than cmd_line() we need to provide
917 # our own mechanism for detecting when the process should be stopped.
918 if self._current_cmd_line is None:
919 self._current_android_cmd_line = None
920 if new_cmd_line != self._current_android_cmd_line:
921 self.stop()
922 self._current_android_cmd_line = new_cmd_line
923
924 super(ChromiumAndroidDriver, self).start(pixel_tests, per_test_args)
925
926 def _start(self, pixel_tests, per_test_args):
927 self._setup_test()
928
929 for retries in range(3):
930 if self._start_once(pixel_tests, per_test_args):
931 return
932 self._log_error('Failed to start the content_shell application. Retr ies=%d. Log:%s' % (retries, self._get_logcat()))
933 self.stop()
934 time.sleep(2)
935 self._abort('Failed to start the content_shell application multiple time s. Giving up.')
936
937 def _start_once(self, pixel_tests, per_test_args):
938 super(ChromiumAndroidDriver, self)._start(pixel_tests, per_test_args)
939
940 self._log_debug('Starting forwarder')
941 self._forwarder_process = self._port._server_process_constructor(
942 self._port, 'Forwarder', self._android_commands.adb_command() + ['sh ell', '%s -D %s' % (self._driver_details.device_forwarder_path(), FORWARD_PORTS) ])
943 self._forwarder_process.start()
944
945 self._android_commands.run(['logcat', '-c'])
946 self._android_commands.run(['shell', 'echo'] + self._android_driver_cmd_ line(pixel_tests, per_test_args) + ['>', self._driver_details.command_line_file( )])
947 start_result = self._android_commands.run(['shell', 'am', 'start', '-e', 'RunInSubThread', '-n', self._driver_details.activity_name()])
948 if start_result.find('Exception') != -1:
949 self._log_error('Failed to start the content_shell application. Exce ption:\n' + start_result)
950 return False
951
952 if not ChromiumAndroidDriver._loop_with_timeout(self._all_pipes_created, DRIVER_START_STOP_TIMEOUT_SECS):
953 return False
954
955 # Read back the shell prompt to ensure adb shell ready.
956 deadline = time.time() + DRIVER_START_STOP_TIMEOUT_SECS
957 self._server_process.start()
958 self._read_prompt(deadline)
959 self._log_debug('Interactive shell started')
960
961 # Start a process to read from the stdout fifo of the test driver and pr int to stdout.
962 self._log_debug('Redirecting stdout to ' + self._out_fifo_path)
963 self._read_stdout_process = self._port._server_process_constructor(
964 self._port, 'ReadStdout', self._android_commands.adb_command() + ['s hell', 'cat', self._out_fifo_path])
965 self._read_stdout_process.start()
966
967 # Start a process to read from the stderr fifo of the test driver and pr int to stdout.
968 self._log_debug('Redirecting stderr to ' + self._err_fifo_path)
969 self._read_stderr_process = self._port._server_process_constructor(
970 self._port, 'ReadStderr', self._android_commands.adb_command() + ['s hell', 'cat', self._err_fifo_path])
971 self._read_stderr_process.start()
972
973 self._log_debug('Redirecting stdin to ' + self._in_fifo_path)
974 self._server_process.write('cat >%s\n' % self._in_fifo_path)
975
976 # Combine the stdout and stderr pipes into self._server_process.
977 self._server_process.replace_outputs(self._read_stdout_process._proc.std out, self._read_stderr_process._proc.stdout)
978
979 def deadlock_detector(processes, normal_startup_event):
980 if not ChromiumAndroidDriver._loop_with_timeout(lambda: normal_start up_event.is_set(), DRIVER_START_STOP_TIMEOUT_SECS):
981 # If normal_startup_event is not set in time, the main thread mu st be blocked at
982 # reading/writing the fifo. Kill the fifo reading/writing proces ses to let the
983 # main thread escape from the deadlocked state. After that, the main thread will
984 # treat this as a crash.
985 self._log_error('Deadlock detected. Processes killed.')
986 for i in processes:
987 i.kill()
988
989 # Start a thread to kill the pipe reading/writing processes on deadlock of the fifos during startup.
990 normal_startup_event = threading.Event()
991 threading.Thread(name='DeadlockDetector', target=deadlock_detector,
992 args=([self._server_process, self._read_stdout_process, self._read_stderr_process], normal_startup_event)).start()
993
994 output = ''
995 line = self._server_process.read_stdout_line(deadline)
996 while not self._server_process.timed_out and not self.has_crashed() and line.rstrip() != '#READY':
997 output += line
998 line = self._server_process.read_stdout_line(deadline)
999
1000 if self._server_process.timed_out and not self.has_crashed():
1001 # The test driver crashed during startup, or when the deadlock detec tor hit
1002 # a deadlock and killed the fifo reading/writing processes.
1003 _log.error('Failed to start the test driver: \n%s' % output)
1004 return False
1005
1006 # Inform the deadlock detector that the startup is successful without de adlock.
1007 normal_startup_event.set()
1008 return True
1009
1010 def _pid_from_android_ps_output(self, ps_output, package_name):
1011 # ps output seems to be fixed width, we only care about the name and the pid
1012 # u0_a72 21630 125 947920 59364 ffffffff 400beee4 S org.chromium.na tive_test
1013 for line in ps_output.split('\n'):
1014 if line.find(self._driver_details.package_name()) != -1:
1015 match = re.match(r'\S+\s+(\d+)', line)
1016 return int(match.group(1))
1017
1018 def _pid_on_target(self):
1019 # FIXME: There must be a better way to do this than grepping ps output!
1020 ps_output = self._android_commands.run(['shell', 'ps'])
1021 return self._pid_from_android_ps_output(ps_output, self._driver_details. package_name())
1022
1023 def stop(self):
1024 self._android_commands.run(['shell', 'am', 'force-stop', self._driver_de tails.package_name()])
1025
1026 if self._read_stdout_process:
1027 self._read_stdout_process.kill()
1028 self._read_stdout_process = None
1029
1030 if self._read_stderr_process:
1031 self._read_stderr_process.kill()
1032 self._read_stderr_process = None
1033
1034 super(ChromiumAndroidDriver, self).stop()
1035
1036 if self._forwarder_process:
1037 self._forwarder_process.kill()
1038 self._forwarder_process = None
1039
1040 if self._android_devices.is_device_prepared(self._android_commands.get_s erial()):
1041 if not ChromiumAndroidDriver._loop_with_timeout(self._remove_all_pip es, DRIVER_START_STOP_TIMEOUT_SECS):
1042 raise AssertionError('Failed to remove fifo files. May be locked .')
1043
1044 def _command_from_driver_input(self, driver_input):
1045 command = super(ChromiumAndroidDriver, self)._command_from_driver_input( driver_input)
1046 if command.startswith('/'):
1047 fs = self._port._filesystem
1048 # FIXME: what happens if command lies outside of the layout_tests_di r on the host?
1049 relative_test_filename = fs.relpath(command, fs.dirname(self._port.l ayout_tests_dir()))
1050 command = DEVICE_WEBKIT_BASE_DIR + relative_test_filename
1051 return command
1052
1053 def _read_prompt(self, deadline):
1054 last_char = ''
1055 while True:
1056 current_char = self._server_process.read_stdout(deadline, 1)
1057 if current_char == ' ':
1058 if last_char in ('#', '$'):
1059 return
1060 last_char = current_char
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698