Source/WebCore: IndexedDB: Introduce putWithIndexKeys and calculate them in the renderer
[WebKit-https.git] / Tools / Scripts / webkitpy / layout_tests / port / chromium_android.py
1 #!/usr/bin/env python
2 # Copyright (C) 2012 Google Inc. All rights reserved.
3 #
4 # Redistribution and use in source and binary forms, with or without
5 # modification, are permitted provided that the following conditions are
6 # met:
7 #
8 #     * Redistributions of source code must retain the above copyright
9 # notice, this list of conditions and the following disclaimer.
10 #     * Redistributions in binary form must reproduce the above
11 # copyright notice, this list of conditions and the following disclaimer
12 # in the documentation and/or other materials provided with the
13 # distribution.
14 #     * Neither the name of Google Inc. nor the names of its
15 # contributors may be used to endorse or promote products derived from
16 # this software without specific prior written permission.
17 #
18 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 import logging
31 import os
32 import shlex
33 import threading
34 import time
35
36 from webkitpy.layout_tests.port import chromium
37 from webkitpy.layout_tests.port import factory
38 from webkitpy.layout_tests.port import server_process
39 from webkitpy.layout_tests.port import webkit
40
41
42 _log = logging.getLogger(__name__)
43
44
45 # The root directory for test resources, which has the same structure as the
46 # source root directory of Chromium.
47 # This path is defined in base/base_paths_android.cc and
48 # webkit/support/platform_support_android.cc.
49 DEVICE_SOURCE_ROOT_DIR = '/data/local/tmp/'
50 COMMAND_LINE_FILE = DEVICE_SOURCE_ROOT_DIR + 'chrome-native-tests-command-line'
51
52 # The directory to put tools and resources of DumpRenderTree.
53 DEVICE_DRT_DIR = '/data/drt/'
54 DEVICE_FORWARDER_PATH = DEVICE_DRT_DIR + 'forwarder'
55 DEVICE_DRT_STAMP_PATH = DEVICE_DRT_DIR + 'DumpRenderTree.stamp'
56
57 DRT_APP_PACKAGE = 'org.chromium.native_test'
58 DRT_ACTIVITY_FULL_NAME = DRT_APP_PACKAGE + '/.ChromeNativeTestActivity'
59 DRT_APP_DIR = '/data/user/0/' + DRT_APP_PACKAGE + '/'
60 DRT_APP_FILES_DIR = DRT_APP_DIR + 'files/'
61 DRT_APP_CACHE_DIR = DRT_APP_DIR + 'cache/'
62
63 # This only works for single core devices so far.
64 # FIXME: Find a solution for multi-core devices.
65 SCALING_GOVERNOR = "/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
66
67 # All the test cases are still served to DumpRenderTree through file protocol,
68 # but we use a file-to-http feature to bridge the file request to host's http
69 # server to get the real test files and corresponding resources.
70 TEST_PATH_PREFIX = '/all-tests'
71
72 # All ports the Android forwarder to forward.
73 # 8000, 8080 and 8443 are for http/https tests.
74 # 8880 and 9323 are for websocket tests
75 # (see http_server.py, apache_http_server.py and websocket_server.py).
76 FORWARD_PORTS = '8000 8080 8443 8880 9323'
77
78 MS_TRUETYPE_FONTS_DIR = '/usr/share/fonts/truetype/msttcorefonts/'
79
80 # Timeout in seconds to wait for start/stop of DumpRenderTree.
81 DRT_START_STOP_TIMEOUT_SECS = 10
82
83 # List of fonts that layout tests expect, copied from DumpRenderTree/gtk/TestShellX11.cpp.
84 HOST_FONT_FILES = [
85     [MS_TRUETYPE_FONTS_DIR, 'Arial.ttf'],
86     [MS_TRUETYPE_FONTS_DIR, 'Arial_Bold.ttf'],
87     [MS_TRUETYPE_FONTS_DIR, 'Arial_Bold_Italic.ttf'],
88     [MS_TRUETYPE_FONTS_DIR, 'Arial_Italic.ttf'],
89     [MS_TRUETYPE_FONTS_DIR, 'Comic_Sans_MS.ttf'],
90     [MS_TRUETYPE_FONTS_DIR, 'Comic_Sans_MS_Bold.ttf'],
91     [MS_TRUETYPE_FONTS_DIR, 'Courier_New.ttf'],
92     [MS_TRUETYPE_FONTS_DIR, 'Courier_New_Bold.ttf'],
93     [MS_TRUETYPE_FONTS_DIR, 'Courier_New_Bold_Italic.ttf'],
94     [MS_TRUETYPE_FONTS_DIR, 'Courier_New_Italic.ttf'],
95     [MS_TRUETYPE_FONTS_DIR, 'Georgia.ttf'],
96     [MS_TRUETYPE_FONTS_DIR, 'Georgia_Bold.ttf'],
97     [MS_TRUETYPE_FONTS_DIR, 'Georgia_Bold_Italic.ttf'],
98     [MS_TRUETYPE_FONTS_DIR, 'Georgia_Italic.ttf'],
99     [MS_TRUETYPE_FONTS_DIR, 'Impact.ttf'],
100     [MS_TRUETYPE_FONTS_DIR, 'Trebuchet_MS.ttf'],
101     [MS_TRUETYPE_FONTS_DIR, 'Trebuchet_MS_Bold.ttf'],
102     [MS_TRUETYPE_FONTS_DIR, 'Trebuchet_MS_Bold_Italic.ttf'],
103     [MS_TRUETYPE_FONTS_DIR, 'Trebuchet_MS_Italic.ttf'],
104     [MS_TRUETYPE_FONTS_DIR, 'Times_New_Roman.ttf'],
105     [MS_TRUETYPE_FONTS_DIR, 'Times_New_Roman_Bold.ttf'],
106     [MS_TRUETYPE_FONTS_DIR, 'Times_New_Roman_Bold_Italic.ttf'],
107     [MS_TRUETYPE_FONTS_DIR, 'Times_New_Roman_Italic.ttf'],
108     [MS_TRUETYPE_FONTS_DIR, 'Verdana.ttf'],
109     [MS_TRUETYPE_FONTS_DIR, 'Verdana_Bold.ttf'],
110     [MS_TRUETYPE_FONTS_DIR, 'Verdana_Bold_Italic.ttf'],
111     [MS_TRUETYPE_FONTS_DIR, 'Verdana_Italic.ttf'],
112     # The Microsoft font EULA
113     ['/usr/share/doc/ttf-mscorefonts-installer/', 'READ_ME!.gz'],
114     ['/usr/share/fonts/truetype/ttf-dejavu/', 'DejaVuSans.ttf'],
115 ]
116 # Should increase this version after changing HOST_FONT_FILES.
117 FONT_FILES_VERSION = 2
118
119 DEVICE_FONTS_DIR = DEVICE_DRT_DIR + 'fonts/'
120
121 # The layout tests directory on device, which has two usages:
122 # 1. as a virtual path in file urls that will be bridged to HTTP.
123 # 2. pointing to some files that are pushed to the device for tests that
124 # don't work on file-over-http (e.g. blob protocol tests).
125 DEVICE_LAYOUT_TESTS_DIR = DEVICE_SOURCE_ROOT_DIR + 'third_party/WebKit/LayoutTests/'
126
127 # Test resources that need to be accessed as files directly.
128 # Each item can be the relative path of a directory or a file.
129 TEST_RESOURCES_TO_PUSH = [
130     # Blob tests need to access files directly.
131     'editing/pasteboard/resources',
132     'fast/files/resources',
133     'http/tests/local/resources',
134     'http/tests/local/formdata/resources',
135     # User style URLs are accessed as local files in webkit_support.
136     'http/tests/security/resources/cssStyle.css',
137     # Media tests need to access audio/video as files.
138     'media/content',
139     'compositing/resources/video.mp4',
140 ]
141
142
143 class ChromiumAndroidPort(chromium.ChromiumPort):
144     port_name = 'chromium-android'
145
146     FALLBACK_PATHS = [
147         'chromium-android',
148         'chromium-linux',
149         'chromium-win',
150         'chromium',
151         'win',
152         'mac',
153     ]
154
155     def __init__(self, host, port_name, **kwargs):
156         super(ChromiumAndroidPort, self).__init__(host, port_name, **kwargs)
157
158         # The Chromium port for Android always uses the hardware GPU path.
159         self._options.enable_hardware_gpu = True
160
161         self._operating_system = 'android'
162         self._version = 'icecreamsandwich'
163         self._original_governor = None
164         self._android_base_dir = None
165
166         self._host_port = factory.PortFactory(host).get('chromium', **kwargs)
167
168         self._adb_command = ['adb']
169         adb_args = self.get_option('adb_args')
170         if adb_args:
171             self._adb_command += shlex.split(adb_args)
172         self._drt_retry_after_killed = 0
173
174     def default_timeout_ms(self):
175         # Android platform has less computing power than desktop platforms.
176         # Using 10 seconds allows us to pass most slow tests which are not
177         # marked as slow tests on desktop platforms.
178         return 10 * 1000
179
180     def default_child_processes(self):
181         # Because of the nature of apk, we don't support more than one process.
182         return 1
183
184     def baseline_search_path(self):
185         return map(self._webkit_baseline_path, self.FALLBACK_PATHS)
186
187     def check_wdiff(self, logging=True):
188         return self._host_port.check_wdiff(logging)
189
190     def check_build(self, needs_http):
191         result = super(ChromiumAndroidPort, self).check_build(needs_http)
192         result = self.check_wdiff() and result
193         if not result:
194             _log.error('For complete Android build requirements, please see:')
195             _log.error('')
196             _log.error('    http://code.google.com/p/chromium/wiki/AndroidBuildInstructions')
197
198         return result
199
200     def check_sys_deps(self, needs_http):
201         for (font_dir, font_file) in HOST_FONT_FILES:
202             font_path = font_dir + font_file
203             if not self._check_file_exists(font_path, 'font file'):
204                 _log.error('You are missing %s. Try installing msttcorefonts. '
205                            'See build instructions.' % font_path)
206                 return False
207         return True
208
209     # FIXME: Remove this function when chromium-android is fully upstream.
210     def expectations_files(self):
211         android_expectations_file = self.path_from_webkit_base('LayoutTests', 'platform', 'chromium', 'test_expectations_android.txt')
212         return super(ChromiumAndroidPort, self).expectations_files() + [android_expectations_file]
213
214     def test_expectations(self):
215         # Automatically apply all expectation rules of chromium-linux to
216         # chromium-android.
217         # FIXME: This is a temporary measure to reduce the manual work when
218         # updating WebKit. This method should be removed when we merge
219         # test_expectations_android.txt into TestExpectations.
220         expectations = super(ChromiumAndroidPort, self).test_expectations()
221         return expectations.replace('LINUX ', 'LINUX ANDROID ')
222
223     def start_http_server(self, additional_dirs=None, number_of_servers=0):
224         # The http server runs during the whole testing period, so ignore this call.
225         pass
226
227     def stop_http_server(self):
228         # Same as start_http_server().
229         pass
230
231     def setup_test_run(self):
232         self._run_adb_command(['root'])
233         self._setup_performance()
234         # Required by webkit_support::GetWebKitRootDirFilePath().
235         # Other directories will be created automatically by adb push.
236         self._run_adb_command(['shell', 'mkdir', '-p', DEVICE_SOURCE_ROOT_DIR + 'chrome'])
237         # Allow the DumpRenderTree app to fully access the directory.
238         # The native code needs the permission to write temporary files here.
239         self._run_adb_command(['shell', 'chmod', '777', DEVICE_SOURCE_ROOT_DIR])
240
241         self._push_executable()
242         self._push_fonts()
243         self._synchronize_datetime()
244
245         # Delete the disk cache if any to ensure a clean test run.
246         # This is like what's done in ChromiumPort.setup_test_run but on the device.
247         self._run_adb_command(['shell', 'rm', '-r', DRT_APP_CACHE_DIR])
248
249         # Start the HTTP server so that the device can access the test cases.
250         super(ChromiumAndroidPort, self).start_http_server(additional_dirs={TEST_PATH_PREFIX: self.layout_tests_dir()})
251
252         _log.debug('Starting forwarder')
253         self._run_adb_command(['shell', '%s %s' % (DEVICE_FORWARDER_PATH, FORWARD_PORTS)])
254
255     def clean_up_test_run(self):
256         # Leave the forwarder and tests httpd server there because they are
257         # useful for debugging and do no harm to subsequent tests.
258         self._teardown_performance()
259
260     def skipped_layout_tests(self, test_list):
261         return self._real_tests([
262             # Canvas tests are run as virtual gpu tests.
263             'fast/canvas',
264             'canvas/philip',
265         ])
266
267     def create_driver(self, worker_number, no_timeout=False):
268         # We don't want the default DriverProxy which is not compatible with our driver.
269         # See comments in ChromiumAndroidDriver.start().
270         return ChromiumAndroidDriver(self, worker_number, pixel_tests=self.get_option('pixel_tests'), no_timeout=no_timeout)
271
272     # Overridden private functions.
273
274     def _build_path(self, *comps):
275         return self._host_port._build_path(*comps)
276
277     def _path_to_apache(self):
278         return self._host_port._path_to_apache()
279
280     def _path_to_apache_config_file(self):
281         return self._host_port._path_to_apache_config_file()
282
283     def _path_to_driver(self, configuration=None):
284         if not configuration:
285             configuration = self.get_option('configuration')
286         return self._build_path(configuration, 'DumpRenderTree_apk/DumpRenderTree-debug.apk')
287
288     def _path_to_helper(self):
289         return None
290
291     def _path_to_forwarder(self):
292         return self._build_path(self.get_option('configuration'), 'forwarder')
293
294     def _path_to_image_diff(self):
295         return self._host_port._path_to_image_diff()
296
297     def _path_to_lighttpd(self):
298         return self._host_port._path_to_lighttpd()
299
300     def _path_to_lighttpd_modules(self):
301         return self._host_port._path_to_lighttpd_modules()
302
303     def _path_to_lighttpd_php(self):
304         return self._host_port._path_to_lighttpd_php()
305
306     def _path_to_wdiff(self):
307         return self._host_port._path_to_wdiff()
308
309     def _shut_down_http_server(self, pid):
310         return self._host_port._shut_down_http_server(pid)
311
312     def _driver_class(self):
313         return ChromiumAndroidDriver
314
315     def _get_crash_log(self, name, pid, stdout, stderr, newer_than):
316         if not stdout:
317             stdout = ''
318         stdout += '********* Logcat:\n' + self._get_logcat()
319         if not stderr:
320             stderr = ''
321         stderr += '********* Tombstone file:\n' + self._get_last_stacktrace()
322         return super(ChromiumAndroidPort, self)._get_crash_log(name, pid, stdout, stderr, newer_than)
323
324     # Local private functions.
325
326     def _push_executable(self):
327         drt_host_path = self._path_to_driver()
328         forwarder_host_path = self._path_to_forwarder()
329         host_stamp = int(float(max(os.stat(drt_host_path).st_mtime,
330                                    os.stat(forwarder_host_path).st_mtime)))
331         device_stamp = int(float(self._run_adb_command([
332             'shell', 'cat %s 2>/dev/null || echo 0' % DEVICE_DRT_STAMP_PATH])))
333         if device_stamp < host_stamp:
334             _log.debug('Pushing executable')
335             self._push_to_device(forwarder_host_path, DEVICE_FORWARDER_PATH)
336             self._run_adb_command(['uninstall', DRT_APP_PACKAGE])
337             install_result = self._run_adb_command(['install', drt_host_path])
338             if install_result.find('Success') == -1:
339                 raise AssertionError('Failed to install %s onto device: %s' % (drt_host_path, install_result))
340             self._push_to_device(self._build_path(self.get_option('configuration'), 'DumpRenderTree.pak'),
341                                  DEVICE_DRT_DIR + 'DumpRenderTree.pak')
342             self._push_to_device(self._build_path(self.get_option('configuration'), 'DumpRenderTree_resources'),
343                                  DEVICE_DRT_DIR + 'DumpRenderTree_resources')
344             self._push_to_device(self._build_path(self.get_option('configuration'), 'android_main_fonts.xml'),
345                                  DEVICE_DRT_DIR + 'android_main_fonts.xml')
346             self._push_to_device(self._build_path(self.get_option('configuration'), 'android_fallback_fonts.xml'),
347                                  DEVICE_DRT_DIR + 'android_fallback_fonts.xml')
348             # Version control of test resources is dependent on executables,
349             # because we will always rebuild executables when resources are
350             # updated.
351             self._push_test_resources()
352             self._run_adb_command(['shell', 'echo %d >%s' % (host_stamp, DEVICE_DRT_STAMP_PATH)])
353
354     def _push_fonts(self):
355         if not self._check_version(DEVICE_FONTS_DIR, FONT_FILES_VERSION):
356             _log.debug('Pushing fonts')
357             path_to_ahem_font = self._build_path(self.get_option('configuration'), 'AHEM____.TTF')
358             self._push_to_device(path_to_ahem_font, DEVICE_FONTS_DIR + 'AHEM____.TTF')
359             for (host_dir, font_file) in HOST_FONT_FILES:
360                 self._push_to_device(host_dir + font_file, DEVICE_FONTS_DIR + font_file)
361             self._link_device_file('/system/fonts/DroidSansFallback.ttf', DEVICE_FONTS_DIR + 'DroidSansFallback.ttf')
362             self._update_version(DEVICE_FONTS_DIR, FONT_FILES_VERSION)
363
364     def _push_test_resources(self):
365         _log.debug('Pushing test resources')
366         for resource in TEST_RESOURCES_TO_PUSH:
367             self._push_to_device(self.layout_tests_dir() + '/' + resource, DEVICE_LAYOUT_TESTS_DIR + resource)
368
369     def _synchronize_datetime(self):
370         # The date/time between host and device may not be synchronized.
371         # We need to make them synchronized, otherwise tests might fail.
372         try:
373             # Get seconds since 1970-01-01 00:00:00 UTC.
374             host_datetime = self._executive.run_command(['date', '-u', '+%s'])
375         except:
376             # Reset to 1970-01-01 00:00:00 UTC.
377             host_datetime = 0
378         self._run_adb_command(['shell', 'date -u %s' % (host_datetime)])
379
380     def _check_version(self, dir, version):
381         assert(dir.endswith('/'))
382         try:
383             device_version = int(self._run_adb_command(['shell', 'cat %sVERSION || echo 0' % dir]))
384             return device_version == version
385         except:
386             return False
387
388     def _update_version(self, dir, version):
389         self._run_adb_command(['shell', 'echo %d > %sVERSION' % (version, dir)])
390
391     def _run_adb_command(self, cmd, ignore_error=False):
392         _log.debug('Run adb command: ' + str(cmd))
393         if ignore_error:
394             error_handler = self._executive.ignore_error
395         else:
396             error_handler = None
397         result = self._executive.run_command(self._adb_command + cmd, error_handler=error_handler)
398         _log.debug('Run adb result:\n' + result)
399         return result
400
401     def _link_device_file(self, from_file, to_file, ignore_error=False):
402         # rm to_file first to make sure that ln succeeds.
403         self._run_adb_command(['shell', 'rm', to_file], ignore_error)
404         return self._run_adb_command(['shell', 'ln', '-s', from_file, to_file], ignore_error)
405
406     def _push_to_device(self, host_path, device_path, ignore_error=False):
407         return self._run_adb_command(['push', host_path, device_path], ignore_error)
408
409     def _pull_from_device(self, device_path, host_path, ignore_error=False):
410         return self._run_adb_command(['pull', device_path, host_path], ignore_error)
411
412     def _get_last_stacktrace(self):
413         tombstones = self._run_adb_command(['shell', 'ls', '-n', '/data/tombstones'])
414         if not tombstones or tombstones.startswith('/data/tombstones: No such file or directory'):
415             _log.error('DRT crashed, but no tombstone found!')
416             return ''
417         tombstones = tombstones.rstrip().split('\n')
418         last_tombstone = tombstones[0].split()
419         for tombstone in tombstones[1:]:
420             # Format of fields:
421             # 0          1      2      3     4          5     6
422             # permission uid    gid    size  date       time  filename
423             # -rw------- 1000   1000   45859 2011-04-13 06:00 tombstone_00
424             fields = tombstone.split()
425             if (fields[4] + fields[5] >= last_tombstone[4] + last_tombstone[5]):
426                 last_tombstone = fields
427             else:
428                 break
429
430         # Use Android tool vendor/google/tools/stack to convert the raw
431         # stack trace into a human readable format, if needed.
432         # It takes a long time, so don't do it here.
433         return '%s\n%s' % (' '.join(last_tombstone),
434                            self._run_adb_command(['shell', 'cat', '/data/tombstones/' + last_tombstone[6]]))
435
436     def _get_logcat(self):
437         return self._run_adb_command(['logcat', '-d'])
438
439     def _setup_performance(self):
440         # Disable CPU scaling and drop ram cache to reduce noise in tests
441         if not self._original_governor:
442             self._original_governor = self._run_adb_command(['shell', 'cat', SCALING_GOVERNOR], ignore_error=True)
443             if self._original_governor:
444                 self._run_adb_command(['shell', 'echo', 'performance', '>', SCALING_GOVERNOR])
445
446     def _teardown_performance(self):
447         if self._original_governor:
448             self._run_adb_command(['shell', 'echo', self._original_governor, SCALING_GOVERNOR])
449         self._original_governor = None
450
451
452 class ChromiumAndroidDriver(webkit.WebKitDriver):
453     def __init__(self, port, worker_number, pixel_tests, no_timeout=False):
454         webkit.WebKitDriver.__init__(self, port, worker_number, pixel_tests, no_timeout)
455         self._pixel_tests = pixel_tests
456         self._in_fifo_path = DRT_APP_FILES_DIR + 'DumpRenderTree.in'
457         self._out_fifo_path = DRT_APP_FILES_DIR + 'DumpRenderTree.out'
458         self._err_fifo_path = DRT_APP_FILES_DIR + 'DumpRenderTree.err'
459         self._restart_after_killed = False
460         self._read_stdout_process = None
461         self._read_stderr_process = None
462
463     def _command_wrapper(cls, wrapper_option):
464         # Ignore command wrapper which is not applicable on Android.
465         return []
466
467     def cmd_line(self, pixel_tests, per_test_args):
468         return self._port._adb_command + ['shell']
469
470     def _file_exists_on_device(self, full_file_path):
471         assert full_file_path.startswith('/')
472         return self._port._run_adb_command(['shell', 'ls', full_file_path]).strip() == full_file_path
473
474     def _deadlock_detector(self, processes, normal_startup_event):
475         time.sleep(DRT_START_STOP_TIMEOUT_SECS)
476         if not normal_startup_event.is_set():
477             # If normal_startup_event is not set in time, the main thread must be blocked at
478             # reading/writing the fifo. Kill the fifo reading/writing processes to let the
479             # main thread escape from the deadlocked state. After that, the main thread will
480             # treat this as a crash.
481             for i in processes:
482                 i.kill()
483         # Otherwise the main thread has been proceeded normally. This thread just exits silently.
484
485     def _drt_cmd_line(self, pixel_tests, per_test_args):
486         return webkit.WebKitDriver.cmd_line(self, pixel_tests, per_test_args) + [
487             '--in-fifo=' + self._in_fifo_path,
488             '--out-fifo=' + self._out_fifo_path,
489             '--err-fifo=' + self._err_fifo_path,
490         ]
491
492     def start(self, pixel_tests, per_test_args):
493         # Only one driver instance is allowed because of the nature of Android activity.
494         # The single driver needs to switch between pixel test and no pixel test mode by itself.
495         if pixel_tests != self._pixel_tests:
496             self.stop()
497         super(ChromiumAndroidDriver, self).start(pixel_tests, per_test_args)
498
499     def _start(self, pixel_tests, per_test_args):
500         retries = 0
501         while not self._start_once(pixel_tests, per_test_args):
502             _log.error('Failed to start DumpRenderTree application. Retries=%d. Log:%s' % (retries, self._port._get_logcat()))
503             retries += 1
504             if retries >= 3:
505                 raise AssertionError('Failed to start DumpRenderTree application multiple times. Give up.')
506             self.stop()
507             time.sleep(2)
508
509     def _start_once(self, pixel_tests, per_test_args):
510         super(ChromiumAndroidDriver, self)._start(pixel_tests, per_test_args)
511
512         self._port._run_adb_command(['logcat', '-c'])
513         self._port._run_adb_command(['shell', 'echo'] + self._drt_cmd_line(pixel_tests, per_test_args) + ['>', COMMAND_LINE_FILE])
514         start_result = self._port._run_adb_command(['shell', 'am', 'start', '-e', 'RunInSubThread', '-n', DRT_ACTIVITY_FULL_NAME])
515         if start_result.find('Exception') != -1:
516             _log.error('Failed to start DumpRenderTree application. Exception:\n' + start_result)
517             return False
518
519         seconds = 0
520         while (not self._file_exists_on_device(self._in_fifo_path) or
521                not self._file_exists_on_device(self._out_fifo_path) or
522                not self._file_exists_on_device(self._err_fifo_path)):
523             time.sleep(1)
524             seconds += 1
525             if seconds >= DRT_START_STOP_TIMEOUT_SECS:
526                 return False
527
528         # Read back the shell prompt to ensure adb shell ready.
529         deadline = time.time() + DRT_START_STOP_TIMEOUT_SECS
530         self._server_process.start()
531         self._read_prompt(deadline)
532         _log.debug('Interactive shell started')
533
534         # Start a process to read from the stdout fifo of the DumpRenderTree app and print to stdout.
535         _log.debug('Redirecting stdout to ' + self._out_fifo_path)
536         self._read_stdout_process = server_process.ServerProcess(
537             self._port, 'ReadStdout', self._port._adb_command + ['shell', 'cat', self._out_fifo_path], universal_newlines=True)
538         self._read_stdout_process.start()
539
540         # Start a process to read from the stderr fifo of the DumpRenderTree app and print to stdout.
541         _log.debug('Redirecting stderr to ' + self._err_fifo_path)
542         self._read_stderr_process = server_process.ServerProcess(
543             self._port, 'ReadStderr', self._port._adb_command + ['shell', 'cat', self._err_fifo_path], universal_newlines=True)
544         self._read_stderr_process.start()
545
546         _log.debug('Redirecting stdin to ' + self._in_fifo_path)
547         self._server_process.write('cat >%s\n' % self._in_fifo_path)
548
549         # Combine the stdout and stderr pipes into self._server_process.
550         self._server_process.replace_outputs(self._read_stdout_process._proc.stdout, self._read_stderr_process._proc.stdout)
551
552         # Start a thread to kill the pipe reading/writing processes on deadlock of the fifos during startup.
553         normal_startup_event = threading.Event()
554         threading.Thread(target=self._deadlock_detector,
555                          args=([self._server_process, self._read_stdout_process, self._read_stderr_process], normal_startup_event)).start()
556
557         output = ''
558         line = self._server_process.read_stdout_line(deadline)
559         while not self._server_process.timed_out and not self.has_crashed() and line.rstrip() != '#READY':
560             output += line
561             line = self._server_process.read_stdout_line(deadline)
562
563         if self._server_process.timed_out and not self.has_crashed():
564             # DumpRenderTree crashes during startup, or when the deadlock detector detected
565             # deadlock and killed the fifo reading/writing processes.
566             _log.error('Failed to start DumpRenderTree: \n%s' % output)
567             return False
568         else:
569             # Inform the deadlock detector that the startup is successful without deadlock.
570             normal_startup_event.set()
571             return True
572
573     def run_test(self, driver_input):
574         driver_output = super(ChromiumAndroidDriver, self).run_test(driver_input)
575         if driver_output.crash:
576             # When Android is OOM, DRT process may be killed by ActivityManager or system OOM.
577             # It looks like a crash but there is no fatal signal logged. Re-run the test for
578             # such crash.
579             # To test: adb shell am force-stop org.chromium.native_test,
580             # or kill -11 pid twice or three times to simulate a fatal crash.
581             if self._port._get_logcat().find('Fatal signal') == -1:
582                 self._restart_after_killed = True
583                 self._port._drt_retry_after_killed += 1
584                 if self._port._drt_retry_after_killed > 10:
585                     raise AssertionError('DumpRenderTree is killed by Android for too many times!')
586                 _log.error('DumpRenderTree is killed by system (%d).' % self._port._drt_retry_after_killed)
587                 self.stop()
588                 # Sleep 10 seconds to let system recover.
589                 time.sleep(10)
590                 return self.run_test(driver_input)
591
592         self._restart_after_killed = False
593         return driver_output
594
595     def stop(self):
596         self._port._run_adb_command(['shell', 'am', 'force-stop', DRT_APP_PACKAGE])
597
598         if self._read_stdout_process:
599             self._read_stdout_process.kill()
600             self._read_stdout_process = None
601
602         if self._read_stderr_process:
603             self._read_stderr_process.kill()
604             self._read_stderr_process = None
605
606         # Stop and kill server_process because our pipe reading/writing processes won't quit
607         # by itself on close of the pipes.
608         if self._server_process:
609             self._server_process.stop(kill_directly=True)
610             self._server_process = None
611         super(ChromiumAndroidDriver, self).stop()
612
613         seconds = 0
614         while (self._file_exists_on_device(self._in_fifo_path) or
615                self._file_exists_on_device(self._out_fifo_path) or
616                self._file_exists_on_device(self._err_fifo_path)):
617             time.sleep(1)
618             self._port._run_adb_command(['shell', 'rm', self._in_fifo_path, self._out_fifo_path, self._err_fifo_path])
619             seconds += 1
620             if seconds >= DRT_START_STOP_TIMEOUT_SECS:
621                 raise AssertionError('Failed to remove fifo files. May be locked.')
622
623     def _command_from_driver_input(self, driver_input):
624         command = super(ChromiumAndroidDriver, self)._command_from_driver_input(driver_input)
625         if command.startswith('/'):
626             # Convert the host file path to a device file path. See comment of
627             # DEVICE_LAYOUT_TESTS_DIR for details.
628             command = DEVICE_LAYOUT_TESTS_DIR + self._port.relative_test_filename(command)
629         return command
630
631     def _read_prompt(self, deadline):
632         last_char = ''
633         while True:
634             current_char = self._server_process.read_stdout(deadline, 1)
635             if current_char == ' ':
636                 if last_char == '#':
637                     return
638                 if last_char == '$':
639                     raise AssertionError('Adbd is not running as root')
640             last_char = current_char