7e9ca197f56e42c9998f8f3926bf42a45211a2ff
[WebKit-https.git] / Tools / Scripts / webkitpy / layout_tests / port / chromium_android.py
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 logging
30 import re
31 import signal
32 import time
33
34 from webkitpy.layout_tests.port import chromium
35 from webkitpy.layout_tests.port import factory
36
37
38 _log = logging.getLogger(__name__)
39
40
41 # The root directory for test resources, which has the same structure as the
42 # source root directory of Chromium.
43 # This path is defined in base/base_paths_android.cc and
44 # webkit/support/platform_support_android.cc.
45 DEVICE_SOURCE_ROOT_DIR = '/data/local/tmp/'
46
47 DEVICE_DRT_DIR = '/data/drt/'
48 DEVICE_DRT_PATH = DEVICE_DRT_DIR + 'DumpRenderTree'
49 DEVICE_DRT_STDERR = DEVICE_DRT_DIR + 'DumpRenderTree.stderr'
50 DEVICE_FORWARDER_PATH = DEVICE_DRT_DIR + 'forwarder'
51 DEVICE_DRT_STAMP_PATH = DEVICE_DRT_DIR + 'DumpRenderTree.stamp'
52
53 # This only works for single core devices so far.
54 # FIXME: Find a solution for multi-core devices.
55 SCALING_GOVERNOR = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
56
57 # All the test cases are still served to DumpRenderTree through file protocol,
58 # but we use a file-to-http feature to bridge the file request to host's http
59 # server to get the real test files and corresponding resources.
60 TEST_PATH_PREFIX = '/all-tests'
61
62 # All ports the Android forwarder to forward.
63 # 8000, 8080 and 8443 are for http/https tests.
64 # 8880 and 9323 are for websocket tests
65 # (see http_server.py, apache_http_server.py and websocket_server.py).
66 FORWARD_PORTS = '8000 8080 8443 8880 9323'
67
68 MS_TRUETYPE_FONTS_DIR = '/usr/share/fonts/truetype/msttcorefonts/'
69
70 # List of fonts that layout tests expect, copied from DumpRenderTree/gtk/TestShellGtk.cpp.
71 HOST_FONT_FILES = [
72     [MS_TRUETYPE_FONTS_DIR, 'Arial.ttf'],
73     [MS_TRUETYPE_FONTS_DIR, 'Arial_Bold.ttf'],
74     [MS_TRUETYPE_FONTS_DIR, 'Arial_Bold_Italic.ttf'],
75     [MS_TRUETYPE_FONTS_DIR, 'Arial_Italic.ttf'],
76     [MS_TRUETYPE_FONTS_DIR, 'Comic_Sans_MS.ttf'],
77     [MS_TRUETYPE_FONTS_DIR, 'Comic_Sans_MS_Bold.ttf'],
78     [MS_TRUETYPE_FONTS_DIR, 'Courier_New.ttf'],
79     [MS_TRUETYPE_FONTS_DIR, 'Courier_New_Bold.ttf'],
80     [MS_TRUETYPE_FONTS_DIR, 'Courier_New_Bold_Italic.ttf'],
81     [MS_TRUETYPE_FONTS_DIR, 'Courier_New_Italic.ttf'],
82     [MS_TRUETYPE_FONTS_DIR, 'Georgia.ttf'],
83     [MS_TRUETYPE_FONTS_DIR, 'Georgia_Bold.ttf'],
84     [MS_TRUETYPE_FONTS_DIR, 'Georgia_Bold_Italic.ttf'],
85     [MS_TRUETYPE_FONTS_DIR, 'Georgia_Italic.ttf'],
86     [MS_TRUETYPE_FONTS_DIR, 'Impact.ttf'],
87     [MS_TRUETYPE_FONTS_DIR, 'Trebuchet_MS.ttf'],
88     [MS_TRUETYPE_FONTS_DIR, 'Trebuchet_MS_Bold.ttf'],
89     [MS_TRUETYPE_FONTS_DIR, 'Trebuchet_MS_Bold_Italic.ttf'],
90     [MS_TRUETYPE_FONTS_DIR, 'Trebuchet_MS_Italic.ttf'],
91     [MS_TRUETYPE_FONTS_DIR, 'Times_New_Roman.ttf'],
92     [MS_TRUETYPE_FONTS_DIR, 'Times_New_Roman_Bold.ttf'],
93     [MS_TRUETYPE_FONTS_DIR, 'Times_New_Roman_Bold_Italic.ttf'],
94     [MS_TRUETYPE_FONTS_DIR, 'Times_New_Roman_Italic.ttf'],
95     [MS_TRUETYPE_FONTS_DIR, 'Verdana.ttf'],
96     [MS_TRUETYPE_FONTS_DIR, 'Verdana_Bold.ttf'],
97     [MS_TRUETYPE_FONTS_DIR, 'Verdana_Bold_Italic.ttf'],
98     [MS_TRUETYPE_FONTS_DIR, 'Verdana_Italic.ttf'],
99     # The Microsoft font EULA
100     ['/usr/share/doc/ttf-mscorefonts-installer/', 'READ_ME!.gz'],
101     ['/usr/share/fonts/truetype/ttf-dejavu/', 'DejaVuSans.ttf'],
102 ]
103 # Should increase this version after changing HOST_FONT_FILES.
104 FONT_FILES_VERSION = 1
105
106 DEVICE_FONTS_DIR = DEVICE_DRT_DIR + 'fonts/'
107 DEVICE_FIRST_FALLBACK_FONT = '/system/fonts/DroidNaskh-Regular.ttf'
108
109 # The layout tests directory on device, which has two usages:
110 # 1. as a virtual path in file urls that will be bridged to HTTP.
111 # 2. pointing to some files that are pushed to the device for tests that
112 # don't work on file-over-http (e.g. blob protocol tests).
113 DEVICE_LAYOUT_TESTS_DIR = (DEVICE_SOURCE_ROOT_DIR + 'third_party/WebKit/LayoutTests/')
114 FILE_TEST_URI_PREFIX = 'file://' + DEVICE_LAYOUT_TESTS_DIR
115
116 # Test resources that need to be accessed as files directly.
117 # Each item can be the relative path of a directory or a file.
118 TEST_RESOURCES_TO_PUSH = [
119     # Blob tests need to access files directly.
120     'editing/pasteboard/resources',
121     'fast/files/resources',
122     'http/tests/local/resources',
123     'http/tests/local/formdata/resources',
124     # User style URLs are accessed as local files in webkit_support.
125     'http/tests/security/resources/cssStyle.css',
126     # Media tests need to access audio/video as files.
127     'media/content',
128     'compositing/resources/video.mp4',
129 ]
130
131
132 class ChromiumAndroidPort(chromium.ChromiumPort):
133     port_name = 'chromium-android'
134
135     FALLBACK_PATHS = [
136         'chromium-android',
137         'chromium-linux',
138         'chromium-win',
139         'chromium',
140         'win',
141         'mac',
142     ]
143
144     def __init__(self, host, port_name, **kwargs):
145         chromium.ChromiumPort.__init__(self, host, port_name, **kwargs)
146
147         self._operating_system = 'android'
148         self._version = 'icecreamsandwich'
149         self._original_governor = None
150         self._android_base_dir = None
151
152         self._host_port = factory.PortFactory(host).get('chromium', **kwargs)
153
154         self._adb_command = ['adb']
155         adb_args = self.get_option('adb_args')
156         if adb_args:
157             self._adb_command += shlex.split(adb_args)
158         self._drt_retry_after_killed = 0
159
160     def default_test_timeout_ms(self):
161         # Android platform has less computing power than desktop platforms.
162         # Using 10 seconds allows us to pass most slow tests which are not
163         # marked as slow tests on desktop platforms.
164         return 10 * 1000
165
166     def default_child_processes(self):
167         # Currently we only use one process, but it might be helpful to use
168         # more that one process in the future to improve performance.
169         return 1
170
171     def baseline_search_path(self):
172         return map(self._webkit_baseline_path, self.FALLBACK_PATHS)
173
174     def check_build(self, needs_http):
175         return self._host_port.check_build(needs_http)
176
177     def check_sys_deps(self, needs_http):
178         for (font_dir, font_file) in HOST_FONT_FILES:
179             font_path = font_dir + font_file
180             if not self._check_file_exists(font_path, 'font file'):
181                 _log.error('You are missing %s. Try installing msttcorefonts. '
182                            'See build instructions.' % font_path)
183                 return False
184         return True
185
186     def test_expectations(self):
187         # Automatically apply all expectation rules of chromium-linux to
188         # chromium-android.
189         # FIXME: This is a temporary measure to reduce the manual work when
190         # updating WebKit. This method should be removed when we merge
191         # test_expectations_android.txt into test_expectations.txt.
192         expectations = chromium.ChromiumPort.test_expectations(self)
193         return expectations.replace('LINUX ', 'LINUX ANDROID ')
194
195     def start_http_server(self, additional_dirs=None):
196         # The http server runs during the whole testing period, so ignore this call.
197         pass
198
199     def stop_http_server(self):
200         # Same as start_http_server().
201         pass
202
203     def start_helper(self):
204         self._setup_performance()
205         # Required by webkit_support::GetWebKitRootDirFilePath().
206         # Other directories will be created automatically by adb push.
207         self._run_adb_command(['shell', 'mkdir', '-p',
208                                DEVICE_SOURCE_ROOT_DIR + 'chrome'])
209
210         self._push_executable()
211         self._push_fonts()
212         self._setup_system_font_for_test()
213         self._synchronize_datetime()
214
215         # Start the HTTP server so that the device can access the test cases.
216         chromium.ChromiumPort.start_http_server(self, additional_dirs={TEST_PATH_PREFIX: self.layout_tests_dir()})
217
218         _log.debug('Starting forwarder')
219         cmd = self._run_adb_command(['shell', '%s %s' % (DEVICE_FORWARDER_PATH, FORWARD_PORTS)])
220
221     def stop_helper(self):
222         self._restore_system_font()
223         # Leave the forwarder and tests httpd server there because they are
224         # useful for debugging and do no harm to subsequent tests.
225         self._teardown_performance()
226
227     def _build_path(self, *comps):
228         return self._host_port._build_path(*comps)
229
230     def _path_to_apache(self):
231         return self._host_port._path_to_apache()
232
233     def _path_to_apache_config_file(self):
234         return self._host_port._path_to_apache_config_file()
235
236     def _path_to_driver(self, configuration=None):
237         # Returns the host path to driver which will be pushed to the device.
238         if not configuration:
239             configuration = self.get_option('configuration')
240         return self._build_path(configuration, 'DumpRenderTree')
241
242     def _path_to_helper(self):
243         return self._build_path(self.get_option('configuration'), 'forwarder')
244
245     def _path_to_image_diff(self):
246         return self._host_port._path_to_image_diff()
247
248     def _path_to_lighttpd(self):
249         return self._host_port._path_to_lighttpd()
250
251     def _path_to_lighttpd_modules(self):
252         return self._host_port._path_to_lighttpd_modules()
253
254     def _path_to_lighttpd_php(self):
255         return self._host_port._path_to_lighttpd_php()
256
257     def _path_to_wdiff(self):
258         return self._host_port._path_to_wdiff()
259
260     def _shut_down_http_server(self, pid):
261         return self._host_port._shut_down_http_server(pid)
262
263     def _driver_class(self):
264         return ChromiumAndroidDriver
265
266     def _push_executable(self):
267         drt_host_path = self._path_to_driver()
268         forwarder_host_path = self._path_to_helper()
269         drt_jar_host_path = drt_host_path + '.jar'
270         host_stamp = int(float(max(os.stat(drt_host_path).st_mtime,
271                                    os.stat(forwarder_host_path).st_mtime,
272                                    os.stat(drt_jar_host_path).st_mtime)))
273         device_stamp = int(float(self._run_adb_command([
274             'shell', 'cat %s 2>/dev/null || echo 0' % DEVICE_DRT_STAMP_PATH])))
275         if device_stamp < host_stamp:
276             _log.debug('Pushing executable')
277             self._kill_device_process(DEVICE_FORWARDER_PATH)
278             self._push_to_device(forwarder_host_path, DEVICE_FORWARDER_PATH)
279             self._push_to_device(drt_host_path, DEVICE_DRT_PATH)
280             self._push_to_device(drt_host_path + '.pak', DEVICE_DRT_PATH + '.pak')
281             self._push_to_device(drt_host_path + '_resources', DEVICE_DRT_PATH + '_resources')
282             self._push_to_device(drt_jar_host_path, DEVICE_DRT_PATH + '.jar')
283             # Version control of test resources is dependent on executables,
284             # because we will always rebuild executables when resources are
285             # updated.
286             self._push_test_resources()
287             self._run_adb_command(['shell', 'echo %d >%s' % (host_stamp, DEVICE_DRT_STAMP_PATH)])
288
289     def _push_fonts(self):
290         if not self._check_version(DEVICE_FONTS_DIR, FONT_FILES_VERSION):
291             _log.debug('Pushing fonts')
292             path_to_ahem_font = self._build_path(self.get_option('configuration'), 'AHEM____.TTF')
293             self._push_to_device(path_to_ahem_font, DEVICE_FONTS_DIR + 'AHEM____.TTF')
294             for (host_dir, font_file) in HOST_FONT_FILES:
295                 self._push_to_device(host_dir + font_file, DEVICE_FONTS_DIR + font_file)
296             self._update_version(DEVICE_FONTS_DIR, FONT_FILES_VERSION)
297
298     def _setup_system_font_for_test(self):
299         # The DejaVu font implicitly used by some CSS 2.1 tests should be added
300         # into the font fallback list of the system. DroidNaskh-Regular.ttf is
301         # the first font in Android Skia's font fallback list. Fortunately the
302         # DejaVu font also contains Naskh glyphs.
303         # First remount /system in read/write mode.
304         self._run_adb_command(['remount'])
305         self._copy_device_file(DEVICE_FONTS_DIR + 'DejaVuSans.ttf', DEVICE_FIRST_FALLBACK_FONT)
306
307     def _restore_system_font(self):
308         # First remount /system in read/write mode.
309         self._run_adb_command(['remount'])
310         self._push_to_device(os.environ['OUT'] + DEVICE_FIRST_FALLBACK_FONT, DEVICE_FIRST_FALLBACK_FONT)
311
312     def _push_test_resources(self):
313         _log.debug('Pushing test resources')
314         for resource in TEST_RESOURCES_TO_PUSH:
315             self._push_to_device(self.layout_tests_dir() + '/' + resource, DEVICE_LAYOUT_TESTS_DIR + resource)
316
317     def _synchronize_datetime(self):
318         # The date/time between host and device may not be synchronized.
319         # We need to make them synchronized, otherwise tests might fail.
320         try:
321             # Get seconds since 1970-01-01 00:00:00 UTC.
322             host_datetime = self._executive.run_command(['date', '-u', '+%s'])
323         except:
324             # Reset to 1970-01-01 00:00:00 UTC.
325             host_datetime = 0
326         self._run_adb_command(['shell', 'date -u %s' % (host_datetime)])
327
328     def _check_version(self, dir, version):
329         assert(dir.endswith('/'))
330         try:
331             device_version = int(self._run_adb_command(['shell', 'cat %sVERSION || echo 0' % dir]))
332             return device_version == version
333         except:
334             return False
335
336     def _update_version(self, dir, version):
337         self._run_adb_command(['shell', 'echo %d > %sVERSION' % (version, dir)])
338
339     def _run_adb_command(self, cmd, ignore_error=False):
340         if ignore_error:
341             error_handler = self._executive.ignore_error
342         else:
343             error_handler = None
344         return self._executive.run_command(self._adb_command + cmd, error_handler=error_handler)
345
346     def _copy_device_file(self, from_file, to_file, ignore_error=False):
347         # 'cp' is unavailable on Android, so use 'dd' instead.
348         return self._run_adb_command(['shell', 'dd', 'if=' + from_file, 'of=' + to_file], ignore_error)
349
350     def _push_to_device(self, host_path, device_path, ignore_error=False):
351         return self._run_adb_command(['push', host_path, device_path], ignore_error)
352
353     def _pull_from_device(self, device_path, host_path, ignore_error=False):
354         return self._run_adb_command(['pull', device_path, host_path], ignore_error)
355
356     def _kill_device_process(self, name):
357         ps_result = self._run_adb_command(['shell', 'ps']).split('\n')
358         for line in ps_result:
359             if line.find(name) > 0:
360                 pid = line.split()[1]
361                 self._run_adb_command(['shell', 'kill', pid])
362
363     def get_stderr(self):
364         return self._run_adb_command(['shell', 'cat', DEVICE_DRT_STDERR], ignore_error=True)
365
366     def get_last_stacktrace(self):
367         tombstones = self._run_adb_command(['shell', 'ls', '-n', '/data/tombstones'])
368         if not tombstones:
369             _log.error('DRT crashed, but no tombstone found!')
370             return ''
371         tombstones = tombstones.rstrip().split('\n')
372         last_tombstone = tombstones[0].split()
373         for tombstone in tombstones[1:]:
374             # Format of fields:
375             # 0          1      2      3     4          5     6
376             # permission uid    gid    size  date       time  filename
377             # -rw------- 1000   1000   45859 2011-04-13 06:00 tombstone_00
378             fields = tombstone.split()
379             if (fields[4] + fields[5] >= last_tombstone[4] + last_tombstone[5]):
380                 last_tombstone = fields
381             else:
382                 break
383
384         # Use Android tool vendor/google/tools/stack to convert the raw
385         # stack trace into a human readable format, if needed.
386         # It takes a long time, so don't do it here.
387         return self._run_adb_command(['shell', 'cat', '/data/tombstones/' + last_tombstone[6]])
388
389     def _setup_performance(self):
390         # Disable CPU scaling and drop ram cache to reduce noise in tests
391         if not self._original_governor:
392             self._original_governor = self._run_adb_command(['shell', 'cat', SCALING_GOVERNOR])
393             self._run_adb_command(['shell', 'echo', 'performance', '>', SCALING_GOVERNOR])
394
395     def _teardown_performance(self):
396         if self._original_governor:
397             self._run_adb_command(['shell', 'echo', self._original_governor, SCALING_GOVERNOR])
398         self._original_governor = None
399
400
401 class ChromiumAndroidDriver(chromium.ChromiumDriver):
402     def __init__(self, port, worker_number, pixel_tests, no_timeout=False):
403         chromium.ChromiumDriver.__init__(self, port, worker_number, pixel_tests, no_timeout)
404         self._device_image_path = None
405         self._drt_return_parser = re.compile('#DRT_RETURN (\d+)')
406
407     def _start(self, pixel_tests, per_test_args):
408         # Convert the original command line into to two parts:
409         # - the 'adb shell' command line to start an interactive adb shell;
410         # - the DumpRenderTree command line to send to the adb shell.
411         original_cmd = self.cmd_line(pixel_tests, per_test_args)
412         shell_cmd = []
413         drt_args = []
414         path_to_driver = self._port._path_to_driver()
415         reading_args_before_driver = True
416         for param in original_cmd:
417             if reading_args_before_driver:
418                 if param == path_to_driver:
419                     reading_args_before_driver = False
420                 else:
421                     shell_cmd.append(param)
422             else:
423                 if param.startswith('--pixel-tests='):
424                     if not self._device_image_path:
425                         self._device_image_path = DEVICE_DRT_DIR + self._port.host.filesystem.basename(self._image_path)
426                     param = '--pixel-tests=' + self._device_image_path
427                 drt_args.append(param)
428
429         shell_cmd += self._port._adb_command
430         shell_cmd.append('shell')
431         retries = 0
432         while True:
433             _log.debug('Starting adb shell for DumpRenderTree: ' + ' '.join(shell_cmd))
434             executive = self._port.host.executive
435             self._proc = executive.popen(shell_cmd, stdin=executive.PIPE, stdout=executive.PIPE, stderr=executive.STDOUT,
436                                          close_fds=True, universal_newlines=True)
437             # Read back the shell prompt to ensure adb shell ready.
438             self._read_prompt()
439             # Some tests rely on this to produce proper number format etc.,
440             # e.g. fast/speech/input-appearance-numberandspeech.html.
441             self._write_command_and_read_line("export LC_CTYPE='en_US'\n")
442             self._write_command_and_read_line("export CLASSPATH='/data/drt/DumpRenderTree.jar'\n")
443
444             # When DumpRenderTree crashes, the Android debuggerd will stop the
445             # process before dumping stack to log/tombstone file and terminating
446             # the process. Sleep 1 second (long enough for debuggerd to dump
447             # stack) before exiting the shell to ensure the process has quit,
448             # otherwise the exit will fail because "You have stopped jobs".
449             drt_cmd = '%s %s 2>%s;echo "#DRT_RETURN $?";sleep 1;exit\n' % (DEVICE_DRT_PATH, ' '.join(drt_args), DEVICE_DRT_STDERR)
450             _log.debug('Starting DumpRenderTree: ' + drt_cmd)
451
452             # Wait until DRT echos '#READY'.
453             output = ''
454             (line, crash) = self._write_command_and_read_line(drt_cmd)
455             while not crash and line.rstrip() != '#READY':
456                 if line == '':  # EOF or crashed
457                     crash = True
458                 else:
459                     output += line
460                     (line, crash) = self._write_command_and_read_line()
461
462             if crash:
463                 # Sometimes the device is in unstable state (may be out of
464                 # memory?) and kills DumpRenderTree just after it is started.
465                 # Try to stop and start it again.
466                 _log.error('Failed to start DumpRenderTree: \n%s\n%s\n' % (output, self._port.get_stderr()))
467                 self.stop()
468                 retries += 1
469                 if retries > 2:
470                     raise AssertionError('Failed multiple times to start DumpRenderTree')
471             else:
472                 return
473
474     def run_test(self, driver_input):
475         driver_output = chromium.ChromiumDriver.run_test(self, driver_input)
476
477         drt_return = self._get_drt_return_value(driver_output.error)
478         if drt_return is not None:
479             _log.debug('DumpRenderTree return value: %d' % drt_return)
480         # FIXME: Retrieve stderr from the target.
481         if driver_output.crash:
482             # When Android is OOM, it sends a SIGKILL signal to DRT. DRT
483             # is stopped silently and regarded as crashed. Re-run the test for
484             # such crash.
485             if drt_return == 128 + signal.SIGKILL:
486                 self._port._drt_retry_after_killed += 1
487                 if self._port._drt_retry_after_killed > 10:
488                     raise AssertionError('DumpRenderTree is killed by Android for too many times!')
489                 _log.error('DumpRenderTree is killed by SIGKILL. Retry the test (%d).' % self._port._drt_retry_after_killed)
490                 self.stop()
491                 # Sleep 10 seconds to let system recover.
492                 time.sleep(10)
493                 return self.run_test(driver_input)
494             # Fetch the stack trace from the tombstone file.
495             # FIXME: sometimes the crash doesn't really happen so that no
496             # tombstone is generated. In that case we fetch the wrong stack
497             # trace.
498             driver_output.error += self._port.get_last_stacktrace().encode('ascii', 'ignore')
499             driver_output.error += self._port._run_adb_command(['logcat', '-d']).encode('ascii', 'ignore')
500         return driver_output
501
502     def stop(self):
503         _log.debug('Stopping DumpRenderTree')
504         if self._proc:
505             # Send an explicit QUIT command because closing the pipe can't let
506             # DumpRenderTree on Android quit immediately.
507             try:
508                 self._proc.stdin.write('QUIT\n')
509             except IOError:
510                 # The pipe has already been closed, indicating abnormal
511                 # situation occurred. Wait a while to allow the device to
512                 # recover. *fingers crossed*
513                 time.sleep(1)
514         chromium.ChromiumDriver.stop(self)
515
516     def _test_shell_command(self, uri, timeout_ms, checksum):
517         if uri.startswith('file:///'):
518             # Convert the host uri to a device uri. See comment of
519             # DEVICE_LAYOUT_TESTS_DIR for details.
520             # Not overriding Port.filename_to_uri() because we don't want the
521             # links in the html report point to device paths.
522             uri = FILE_TEST_URI_PREFIX + self.uri_to_test(uri)
523         return chromium.ChromiumDriver._test_shell_command(self, uri, timeout_ms, checksum)
524
525     def _write_command_and_read_line(self, input=None):
526         (line, crash) = chromium.ChromiumDriver._write_command_and_read_line(self, input)
527         url_marker = '#URL:'
528         if not crash and line.startswith(url_marker) and line.find(FILE_TEST_URI_PREFIX) == len(url_marker):
529             # Convert the device test uri back to host uri otherwise
530             # chromium.ChromiumDriver.run_test() will complain.
531             line = '#URL:file://%s/%s' % (self._port.layout_tests_dir(), line[len(url_marker) + len(FILE_TEST_URI_PREFIX):])
532         if not crash and self._has_crash_hint(line):
533             crash = True
534         return (line, crash)
535
536     def _output_image(self):
537         if self._image_path:
538             _log.debug('pulling from device: %s to %s' % (self._device_image_path, self._image_path))
539             self._port._pull_from_device(self._device_image_path, self._image_path, ignore_error=True)
540         return chromium.ChromiumDriver._output_image(self)
541
542     def _has_crash_hint(self, line):
543         # When DRT crashes, it sends a signal to Android Debuggerd, like
544         # SIGSEGV, SIGFPE, etc. When Debuggerd receives the signal, it stops DRT
545         # (which causes Shell to output a message), and dumps the stack strace.
546         # We use the Shell output as a crash hint.
547         return line is not None and line.find('[1] + Stopped (signal)') >= 0
548
549     def _get_drt_return_value(self, error):
550         return_match = self._drt_return_parser.search(error)
551         return None if (return_match is None) else int(return_match.group(1))
552
553     def _read_prompt(self):
554         last_char = ''
555         while True:
556             current_char = self._proc.stdout.read(1)
557             if current_char == ' ':
558                 if last_char == '#':
559                     return
560                 if last_char == '$':
561                     raise AssertionError('Adbd is not running as root')
562             last_char = current_char