[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
| 6 | import collections |
| 7 | import glob |
[email protected] | 485fb23 | 2013-08-22 19:56:33 | [diff] [blame] | 8 | import hashlib |
[email protected] | f049a19 | 2013-10-10 01:11:26 | [diff] [blame^] | 9 | import json |
[email protected] | 78af871 | 2013-01-14 10:37:12 | [diff] [blame] | 10 | import multiprocessing |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 11 | import os |
[email protected] | 485fb23 | 2013-08-22 19:56:33 | [diff] [blame] | 12 | import random |
[email protected] | e2107bd | 2013-09-18 05:13:10 | [diff] [blame] | 13 | import re |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 14 | import shutil |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 15 | import sys |
| 16 | |
[email protected] | c528275 | 2013-06-07 23:14:39 | [diff] [blame] | 17 | import bb_utils |
[email protected] | b387389 | 2013-07-10 04:57:10 | [diff] [blame] | 18 | import bb_annotations |
[email protected] | c528275 | 2013-06-07 23:14:39 | [diff] [blame] | 19 | |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 20 | sys.path.append(os.path.join(os.path.dirname(__file__), '..')) |
[email protected] | 7849a33 | 2013-07-12 01:40:09 | [diff] [blame] | 21 | import provision_devices |
[email protected] | 78af871 | 2013-01-14 10:37:12 | [diff] [blame] | 22 | from pylib import android_commands |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 23 | from pylib import constants |
[email protected] | a8fea93f | 2013-01-10 04:00:07 | [diff] [blame] | 24 | from pylib.gtest import gtest_config |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 25 | |
[email protected] | dc8ec3f | 2013-09-07 07:18:14 | [diff] [blame] | 26 | CHROME_SRC_DIR = bb_utils.CHROME_SRC |
| 27 | CHROME_OUT_DIR = bb_utils.CHROME_OUT_DIR |
[email protected] | 78af871 | 2013-01-14 10:37:12 | [diff] [blame] | 28 | sys.path.append(os.path.join( |
[email protected] | dc8ec3f | 2013-09-07 07:18:14 | [diff] [blame] | 29 | CHROME_SRC_DIR, 'third_party', 'android_testrunner')) |
[email protected] | 78af871 | 2013-01-14 10:37:12 | [diff] [blame] | 30 | import errors |
| 31 | |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 32 | |
[email protected] | dc8ec3f | 2013-09-07 07:18:14 | [diff] [blame] | 33 | SLAVE_SCRIPTS_DIR = os.path.join(bb_utils.BB_BUILD_DIR, 'scripts', 'slave') |
| 34 | LOGCAT_DIR = os.path.join(bb_utils.CHROME_OUT_DIR, 'logcat') |
[email protected] | 4e622cee | 2013-09-17 18:32:12 | [diff] [blame] | 35 | GS_URL = 'https://storage.googleapis.com' |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 36 | |
| 37 | # Describes an instrumation test suite: |
| 38 | # test: Name of test we're running. |
| 39 | # apk: apk to be installed. |
| 40 | # apk_package: package for the apk to be installed. |
| 41 | # test_apk: apk to run tests on. |
| 42 | # test_data: data folder in format destination:source. |
[email protected] | 37ee0c79 | 2013-08-06 19:10:13 | [diff] [blame] | 43 | # host_driven_root: The host-driven test root directory. |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 44 | # annotation: Annotation of the tests to include. |
| 45 | # exclude_annotation: The annotation of the tests to exclude. |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 46 | I_TEST = collections.namedtuple('InstrumentationTest', [ |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 47 | 'name', 'apk', 'apk_package', 'test_apk', 'test_data', 'host_driven_root', |
| 48 | 'annotation', 'exclude_annotation', 'extra_flags']) |
| 49 | |
[email protected] | f049a19 | 2013-10-10 01:11:26 | [diff] [blame^] | 50 | |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 51 | def I(name, apk, apk_package, test_apk, test_data, host_driven_root=None, |
| 52 | annotation=None, exclude_annotation=None, extra_flags=None): |
| 53 | return I_TEST(name, apk, apk_package, test_apk, test_data, host_driven_root, |
| 54 | annotation, exclude_annotation, extra_flags) |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 55 | |
| 56 | INSTRUMENTATION_TESTS = dict((suite.name, suite) for suite in [ |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 57 | I('ContentShell', |
| 58 | 'ContentShell.apk', |
| 59 | 'org.chromium.content_shell_apk', |
| 60 | 'ContentShellTest', |
| 61 | 'content:content/test/data/android/device_files'), |
| 62 | I('ChromiumTestShell', |
| 63 | 'ChromiumTestShell.apk', |
| 64 | 'org.chromium.chrome.testshell', |
| 65 | 'ChromiumTestShellTest', |
| 66 | 'chrome:chrome/test/data/android/device_files', |
| 67 | constants.CHROMIUM_TEST_SHELL_HOST_DRIVEN_DIR), |
| 68 | I('AndroidWebView', |
| 69 | 'AndroidWebView.apk', |
| 70 | 'org.chromium.android_webview.shell', |
| 71 | 'AndroidWebViewTest', |
| 72 | 'webview:android_webview/test/data/device_files'), |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 73 | ]) |
| 74 | |
[email protected] | bb508f8 | 2013-09-06 06:42:24 | [diff] [blame] | 75 | VALID_TESTS = set(['chromedriver', 'gpu', 'ui', 'unit', 'webkit', |
| 76 | 'webkit_layout', 'webrtc']) |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 77 | |
[email protected] | c528275 | 2013-06-07 23:14:39 | [diff] [blame] | 78 | RunCmd = bb_utils.RunCmd |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 79 | |
| 80 | |
[email protected] | e7bd341 | 2013-09-20 17:15:28 | [diff] [blame] | 81 | def _GetRevision(options): |
| 82 | """Get the SVN revision number. |
| 83 | |
| 84 | Args: |
| 85 | options: options object. |
| 86 | |
| 87 | Returns: |
| 88 | The revision number. |
| 89 | """ |
| 90 | revision = options.build_properties.get('got_revision') |
| 91 | if not revision: |
| 92 | revision = options.build_properties.get('revision', 'testing') |
| 93 | return revision |
| 94 | |
| 95 | |
[email protected] | 78af871 | 2013-01-14 10:37:12 | [diff] [blame] | 96 | # multiprocessing map_async requires a top-level function for pickle library. |
| 97 | def RebootDeviceSafe(device): |
| 98 | """Reboot a device, wait for it to start, and squelch timeout exceptions.""" |
| 99 | try: |
| 100 | android_commands.AndroidCommands(device).Reboot(True) |
| 101 | except errors.DeviceUnresponsiveError as e: |
| 102 | return e |
| 103 | |
| 104 | |
| 105 | def RebootDevices(): |
| 106 | """Reboot all attached and online devices.""" |
[email protected] | c94ae6b | 2013-01-14 21:38:34 | [diff] [blame] | 107 | # Early return here to avoid presubmit dependence on adb, |
| 108 | # which might not exist in this checkout. |
[email protected] | c528275 | 2013-06-07 23:14:39 | [diff] [blame] | 109 | if bb_utils.TESTING: |
[email protected] | c94ae6b | 2013-01-14 21:38:34 | [diff] [blame] | 110 | return |
[email protected] | 30bd291 | 2013-07-20 06:18:11 | [diff] [blame] | 111 | devices = android_commands.GetAttachedDevices(emulator=False) |
[email protected] | 78af871 | 2013-01-14 10:37:12 | [diff] [blame] | 112 | print 'Rebooting: %s' % devices |
[email protected] | c94ae6b | 2013-01-14 21:38:34 | [diff] [blame] | 113 | if devices: |
[email protected] | 78af871 | 2013-01-14 10:37:12 | [diff] [blame] | 114 | pool = multiprocessing.Pool(len(devices)) |
| 115 | results = pool.map_async(RebootDeviceSafe, devices).get(99999) |
| 116 | |
| 117 | for device, result in zip(devices, results): |
| 118 | if result: |
| 119 | print '%s failed to startup.' % device |
| 120 | |
| 121 | if any(results): |
[email protected] | b387389 | 2013-07-10 04:57:10 | [diff] [blame] | 122 | bb_annotations.PrintWarning() |
[email protected] | 78af871 | 2013-01-14 10:37:12 | [diff] [blame] | 123 | else: |
| 124 | print 'Reboots complete.' |
| 125 | |
| 126 | |
[email protected] | a8fea93f | 2013-01-10 04:00:07 | [diff] [blame] | 127 | def RunTestSuites(options, suites): |
[email protected] | fbe2932 | 2013-07-09 09:03:26 | [diff] [blame] | 128 | """Manages an invocation of test_runner.py for gtests. |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 129 | |
| 130 | Args: |
| 131 | options: options object. |
[email protected] | 9e68925 | 2013-07-30 20:14:36 | [diff] [blame] | 132 | suites: List of suite names to run. |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 133 | """ |
| 134 | args = ['--verbose'] |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 135 | if options.target == 'Release': |
| 136 | args.append('--release') |
| 137 | if options.asan: |
| 138 | args.append('--tool=asan') |
[email protected] | a8fea93f | 2013-01-10 04:00:07 | [diff] [blame] | 139 | for suite in suites: |
[email protected] | 9e68925 | 2013-07-30 20:14:36 | [diff] [blame] | 140 | bb_annotations.PrintNamedStep(suite) |
| 141 | cmd = ['build/android/test_runner.py', 'gtest', '-s', suite] + args |
| 142 | if suite == 'content_browsertests': |
| 143 | cmd.append('--num_retries=1') |
[email protected] | 9324bff | 2013-03-13 04:09:27 | [diff] [blame] | 144 | RunCmd(cmd) |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 145 | |
[email protected] | f049a19 | 2013-10-10 01:11:26 | [diff] [blame^] | 146 | |
[email protected] | e7bd341 | 2013-09-20 17:15:28 | [diff] [blame] | 147 | def RunChromeDriverTests(options): |
[email protected] | d8897f6c | 2013-03-05 00:27:16 | [diff] [blame] | 148 | """Run all the steps for running chromedriver tests.""" |
[email protected] | b387389 | 2013-07-10 04:57:10 | [diff] [blame] | 149 | bb_annotations.PrintNamedStep('chromedriver_annotation') |
[email protected] | d8897f6c | 2013-03-05 00:27:16 | [diff] [blame] | 150 | RunCmd(['chrome/test/chromedriver/run_buildbot_steps.py', |
[email protected] | eb9f5a3 | 2013-08-22 19:46:02 | [diff] [blame] | 151 | '--android-packages=%s,%s,%s' % |
[email protected] | f049a19 | 2013-10-10 01:11:26 | [diff] [blame^] | 152 | (constants.PACKAGE_INFO['chromium_test_shell'].package, |
| 153 | constants.PACKAGE_INFO['chrome_stable'].package, |
| 154 | constants.PACKAGE_INFO['chrome_beta'].package), |
[email protected] | e7bd341 | 2013-09-20 17:15:28 | [diff] [blame] | 155 | '--revision=%s' % _GetRevision(options), |
[email protected] | fde0700 | 2013-09-20 21:02:39 | [diff] [blame] | 156 | '--update-log']) |
[email protected] | d8897f6c | 2013-03-05 00:27:16 | [diff] [blame] | 157 | |
[email protected] | f049a19 | 2013-10-10 01:11:26 | [diff] [blame^] | 158 | |
[email protected] | a2a72524 | 2013-01-09 21:52:57 | [diff] [blame] | 159 | def InstallApk(options, test, print_step=False): |
| 160 | """Install an apk to all phones. |
| 161 | |
| 162 | Args: |
| 163 | options: options object |
| 164 | test: An I_TEST namedtuple |
| 165 | print_step: Print a buildbot step |
| 166 | """ |
| 167 | if print_step: |
[email protected] | b387389 | 2013-07-10 04:57:10 | [diff] [blame] | 168 | bb_annotations.PrintNamedStep('install_%s' % test.name.lower()) |
[email protected] | 6ca5751 | 2013-08-23 21:42:04 | [diff] [blame] | 169 | |
[email protected] | a2a72524 | 2013-01-09 21:52:57 | [diff] [blame] | 170 | args = ['--apk', test.apk, '--apk_package', test.apk_package] |
| 171 | if options.target == 'Release': |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 172 | args.append('--release') |
| 173 | |
[email protected] | 800673bf | 2013-05-14 17:15:30 | [diff] [blame] | 174 | RunCmd(['build/android/adb_install_apk.py'] + args, halt_on_failure=True) |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 175 | |
| 176 | |
[email protected] | acfaf4c | 2013-07-25 00:23:56 | [diff] [blame] | 177 | def RunInstrumentationSuite(options, test, flunk_on_failure=True, |
[email protected] | 54c2d53 | 2013-08-24 01:36:24 | [diff] [blame] | 178 | python_only=False, official_build=False): |
[email protected] | fbe2932 | 2013-07-09 09:03:26 | [diff] [blame] | 179 | """Manages an invocation of test_runner.py for instrumentation tests. |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 180 | |
| 181 | Args: |
| 182 | options: options object |
| 183 | test: An I_TEST namedtuple |
[email protected] | acfaf4c | 2013-07-25 00:23:56 | [diff] [blame] | 184 | flunk_on_failure: Flunk the step if tests fail. |
| 185 | Python: Run only host driven Python tests. |
[email protected] | 54c2d53 | 2013-08-24 01:36:24 | [diff] [blame] | 186 | official_build: Run official-build tests. |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 187 | """ |
[email protected] | b387389 | 2013-07-10 04:57:10 | [diff] [blame] | 188 | bb_annotations.PrintNamedStep('%s_instrumentation_tests' % test.name.lower()) |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 189 | |
[email protected] | a2a72524 | 2013-01-09 21:52:57 | [diff] [blame] | 190 | InstallApk(options, test) |
[email protected] | d1a0657c | 2013-04-10 22:38:59 | [diff] [blame] | 191 | args = ['--test-apk', test.test_apk, '--test_data', test.test_data, |
[email protected] | fb7ab5e8 | 2013-07-26 18:31:20 | [diff] [blame] | 192 | '--verbose'] |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 193 | if options.target == 'Release': |
| 194 | args.append('--release') |
| 195 | if options.asan: |
| 196 | args.append('--tool=asan') |
[email protected] | 14f139f4 | 2013-07-24 18:41:58 | [diff] [blame] | 197 | if options.flakiness_server: |
[email protected] | b796a77 | 2013-01-19 03:55:22 | [diff] [blame] | 198 | args.append('--flakiness-dashboard-server=%s' % |
[email protected] | 14f139f4 | 2013-07-24 18:41:58 | [diff] [blame] | 199 | options.flakiness_server) |
[email protected] | 485fb23 | 2013-08-22 19:56:33 | [diff] [blame] | 200 | if options.coverage_bucket: |
| 201 | args.append('--coverage-dir=%s' % options.coverage_dir) |
[email protected] | 8d8ca04 | 2013-02-14 19:29:17 | [diff] [blame] | 202 | if test.host_driven_root: |
[email protected] | 67954f82 | 2013-08-14 18:09:08 | [diff] [blame] | 203 | args.append('--host-driven-root=%s' % test.host_driven_root) |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 204 | if test.annotation: |
| 205 | args.extend(['-A', test.annotation]) |
| 206 | if test.exclude_annotation: |
| 207 | args.extend(['-E', test.exclude_annotation]) |
| 208 | if test.extra_flags: |
| 209 | args.extend(test.extra_flags) |
[email protected] | acfaf4c | 2013-07-25 00:23:56 | [diff] [blame] | 210 | if python_only: |
| 211 | args.append('-p') |
[email protected] | 54c2d53 | 2013-08-24 01:36:24 | [diff] [blame] | 212 | if official_build: |
| 213 | # The option needs to be assigned 'True' as it does not have an action |
| 214 | # associated with it. |
| 215 | args.append('--official-build') |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 216 | |
[email protected] | bdd22ff | 2013-07-17 17:21:12 | [diff] [blame] | 217 | RunCmd(['build/android/test_runner.py', 'instrumentation'] + args, |
| 218 | flunk_on_failure=flunk_on_failure) |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 219 | |
| 220 | |
| 221 | def RunWebkitLint(target): |
| 222 | """Lint WebKit's TestExpectation files.""" |
[email protected] | b387389 | 2013-07-10 04:57:10 | [diff] [blame] | 223 | bb_annotations.PrintNamedStep('webkit_lint') |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 224 | RunCmd(['webkit/tools/layout_tests/run_webkit_tests.py', |
| 225 | '--lint-test-files', |
| 226 | '--chromium', |
| 227 | '--target', target]) |
| 228 | |
| 229 | |
| 230 | def RunWebkitLayoutTests(options): |
| 231 | """Run layout tests on an actual device.""" |
[email protected] | b387389 | 2013-07-10 04:57:10 | [diff] [blame] | 232 | bb_annotations.PrintNamedStep('webkit_tests') |
[email protected] | 22ee700 | 2013-01-23 20:58:04 | [diff] [blame] | 233 | cmd_args = [ |
[email protected] | f049a19 | 2013-10-10 01:11:26 | [diff] [blame^] | 234 | '--no-show-results', |
| 235 | '--no-new-test-results', |
| 236 | '--full-results-html', |
| 237 | '--clobber-old-results', |
| 238 | '--exit-after-n-failures', '5000', |
| 239 | '--exit-after-n-crashes-or-timeouts', '100', |
| 240 | '--debug-rwt-logging', |
| 241 | '--results-directory', '../layout-test-results', |
| 242 | '--target', options.target, |
| 243 | '--builder-name', options.build_properties.get('buildername', ''), |
| 244 | '--build-number', str(options.build_properties.get('buildnumber', '')), |
| 245 | '--master-name', 'ChromiumWebkit', # TODO: Get this from the cfg. |
| 246 | '--build-name', options.build_properties.get('buildername', ''), |
| 247 | '--platform=android'] |
[email protected] | 22ee700 | 2013-01-23 20:58:04 | [diff] [blame] | 248 | |
| 249 | for flag in 'test_results_server', 'driver_name', 'additional_drt_flag': |
| 250 | if flag in options.factory_properties: |
| 251 | cmd_args.extend(['--%s' % flag.replace('_', '-'), |
| 252 | options.factory_properties.get(flag)]) |
| 253 | |
[email protected] | 2c39b29 | 2013-03-20 09:34:10 | [diff] [blame] | 254 | for f in options.factory_properties.get('additional_expectations', []): |
| 255 | cmd_args.extend( |
[email protected] | dc8ec3f | 2013-09-07 07:18:14 | [diff] [blame] | 256 | ['--additional-expectations=%s' % os.path.join(CHROME_SRC_DIR, *f)]) |
[email protected] | 2c39b29 | 2013-03-20 09:34:10 | [diff] [blame] | 257 | |
| 258 | # TODO(dpranke): Remove this block after |
| 259 | # https://codereview.chromium.org/12927002/ lands. |
[email protected] | 22ee700 | 2013-01-23 20:58:04 | [diff] [blame] | 260 | for f in options.factory_properties.get('additional_expectations_files', []): |
[email protected] | 62c5b98 | 2013-01-26 08:23:16 | [diff] [blame] | 261 | cmd_args.extend( |
[email protected] | dc8ec3f | 2013-09-07 07:18:14 | [diff] [blame] | 262 | ['--additional-expectations=%s' % os.path.join(CHROME_SRC_DIR, *f)]) |
[email protected] | 22ee700 | 2013-01-23 20:58:04 | [diff] [blame] | 263 | |
[email protected] | f049a19 | 2013-10-10 01:11:26 | [diff] [blame^] | 264 | exit_code = RunCmd(['webkit/tools/layout_tests/run_webkit_tests.py'] + |
| 265 | cmd_args) |
| 266 | if exit_code == 254: # AKA -1, internal error. |
| 267 | bb_annotations.PrintMsg('?? (crashed or hung)') |
| 268 | else: |
| 269 | full_results_path = os.path.join('..', 'layout-test-results', |
| 270 | 'full_results.json') |
| 271 | if os.path.exists(full_results_path): |
| 272 | full_results = json.load(open(full_results_path)) |
| 273 | unexpected_failures, unexpected_flakes, unexpected_passes = ( |
| 274 | _ParseLayoutTestResults(full_results)) |
| 275 | if unexpected_failures: |
| 276 | _PrintDashboardLink('failed', unexpected_failures, |
| 277 | max_tests=25) |
| 278 | elif unexpected_passes: |
| 279 | _PrintDashboardLink('unexpected passes', unexpected_passes, |
| 280 | max_tests=10) |
| 281 | if unexpected_flakes: |
| 282 | _PrintDashboardLink('unexpected flakes', unexpected_flakes, |
| 283 | max_tests=10) |
| 284 | else: |
| 285 | bb_annotations.PrintMsg('?? (results missing)') |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 286 | |
[email protected] | dc8ec3f | 2013-09-07 07:18:14 | [diff] [blame] | 287 | if options.factory_properties.get('archive_webkit_results', False): |
| 288 | bb_annotations.PrintNamedStep('archive_webkit_results') |
[email protected] | 613e5a3 | 2013-09-18 01:46:32 | [diff] [blame] | 289 | base = 'https://storage.googleapis.com/chromium-layout-test-archives' |
| 290 | builder_name = options.build_properties.get('buildername', '') |
| 291 | build_number = str(options.build_properties.get('buildnumber', '')) |
[email protected] | f049a19 | 2013-10-10 01:11:26 | [diff] [blame^] | 292 | results_link = '%s/%s/%s/layout-test-results/results.html' % ( |
| 293 | base, EscapeBuilderName(builder_name), build_number) |
| 294 | bb_annotations.PrintLink('results', results_link) |
[email protected] | e2107bd | 2013-09-18 05:13:10 | [diff] [blame] | 295 | bb_annotations.PrintLink('(zip)', '%s/%s/%s/layout-test-results.zip' % ( |
| 296 | base, EscapeBuilderName(builder_name), build_number)) |
[email protected] | 85db7daf3 | 2013-09-09 21:21:25 | [diff] [blame] | 297 | gs_bucket = 'gs://chromium-layout-test-archives' |
[email protected] | dc8ec3f | 2013-09-07 07:18:14 | [diff] [blame] | 298 | RunCmd([os.path.join(SLAVE_SCRIPTS_DIR, 'chromium', |
| 299 | 'archive_layout_test_results.py'), |
[email protected] | f049a19 | 2013-10-10 01:11:26 | [diff] [blame^] | 300 | '--results-dir', '../../layout-test-results', |
| 301 | '--build-dir', CHROME_OUT_DIR, |
| 302 | '--build-number', build_number, |
| 303 | '--builder-name', builder_name, |
| 304 | '--gs-bucket', gs_bucket]) |
| 305 | |
| 306 | |
| 307 | def _ParseLayoutTestResults(results): |
| 308 | """Extract the failures from the test run.""" |
| 309 | # Cloned from third_party/WebKit/Tools/Scripts/print-json-test-results |
| 310 | tests = _ConvertTrieToFlatPaths(results['tests']) |
| 311 | failures = {} |
| 312 | flakes = {} |
| 313 | passes = {} |
| 314 | for (test, result) in tests.iteritems(): |
| 315 | if result.get('is_unexpected'): |
| 316 | actual_result = result['actual'] |
| 317 | if ' PASS' in actual_result: |
| 318 | flakes[test] = actual_result |
| 319 | elif actual_result == 'PASS': |
| 320 | passes[test] = result |
| 321 | else: |
| 322 | failures[test] = actual_result |
| 323 | |
| 324 | return (passes, failures, flakes) |
| 325 | |
| 326 | |
| 327 | def _ConvertTrieToFlatPaths(trie, prefix=None): |
| 328 | """Flatten the trie of failures into a list.""" |
| 329 | # Cloned from third_party/WebKit/Tools/Scripts/print-json-test-results |
| 330 | result = {} |
| 331 | for name, data in trie.iteritems(): |
| 332 | if prefix: |
| 333 | name = prefix + '/' + name |
| 334 | |
| 335 | if len(data) and 'actual' not in data and 'expected' not in data: |
| 336 | result.update(_ConvertTrieToFlatPaths(data, name)) |
| 337 | else: |
| 338 | result[name] = data |
| 339 | |
| 340 | return result |
| 341 | |
| 342 | |
| 343 | def _PrintDashboardLink(link_text, tests, max_tests): |
| 344 | """Add a link to the flakiness dashboard in the step annotations.""" |
| 345 | if len(tests) > max_tests: |
| 346 | test_list_text = ' '.join(tests[:max_tests]) + ' and more' |
| 347 | else: |
| 348 | test_list_text = ' '.join(tests) |
| 349 | |
| 350 | dashboard_base = ('http://test-results.appspot.com' |
| 351 | '/dashboards/flakiness_dashboard.html#' |
| 352 | 'master=ChromiumWebkit&tests=') |
| 353 | |
| 354 | bb_annotations.PrintLink('%d %s: %s' % |
| 355 | (len(tests), link_text, test_list_text), |
| 356 | dashboard_base + ','.join(tests)) |
[email protected] | dc8ec3f | 2013-09-07 07:18:14 | [diff] [blame] | 357 | |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 358 | |
[email protected] | e2107bd | 2013-09-18 05:13:10 | [diff] [blame] | 359 | def EscapeBuilderName(builder_name): |
| 360 | return re.sub('[ ()]', '_', builder_name) |
| 361 | |
| 362 | |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 363 | def SpawnLogcatMonitor(): |
| 364 | shutil.rmtree(LOGCAT_DIR, ignore_errors=True) |
[email protected] | 97d1032 | 2013-10-04 20:53:04 | [diff] [blame] | 365 | bb_utils.SpawnCmd([ |
[email protected] | dc8ec3f | 2013-09-07 07:18:14 | [diff] [blame] | 366 | os.path.join(CHROME_SRC_DIR, 'build', 'android', 'adb_logcat_monitor.py'), |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 367 | LOGCAT_DIR]) |
| 368 | |
| 369 | # Wait for logcat_monitor to pull existing logcat |
| 370 | RunCmd(['sleep', '5']) |
| 371 | |
[email protected] | f049a19 | 2013-10-10 01:11:26 | [diff] [blame^] | 372 | |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 373 | def ProvisionDevices(options): |
[email protected] | b387389 | 2013-07-10 04:57:10 | [diff] [blame] | 374 | bb_annotations.PrintNamedStep('provision_devices') |
[email protected] | abfec37 | 2013-08-16 07:22:16 | [diff] [blame] | 375 | |
| 376 | if not bb_utils.TESTING: |
| 377 | # Restart adb to work around bugs, sleep to wait for usb discovery. |
| 378 | adb = android_commands.AndroidCommands() |
| 379 | adb.RestartAdbServer() |
| 380 | RunCmd(['sleep', '1']) |
| 381 | |
[email protected] | 78af871 | 2013-01-14 10:37:12 | [diff] [blame] | 382 | if options.reboot: |
| 383 | RebootDevices() |
[email protected] | 7849a33 | 2013-07-12 01:40:09 | [diff] [blame] | 384 | provision_cmd = ['build/android/provision_devices.py', '-t', options.target] |
| 385 | if options.auto_reconnect: |
| 386 | provision_cmd.append('--auto-reconnect') |
| 387 | RunCmd(provision_cmd) |
[email protected] | 78af871 | 2013-01-14 10:37:12 | [diff] [blame] | 388 | |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 389 | |
| 390 | def DeviceStatusCheck(_): |
[email protected] | b387389 | 2013-07-10 04:57:10 | [diff] [blame] | 391 | bb_annotations.PrintNamedStep('device_status_check') |
[email protected] | 37a6a38 | 2013-07-17 02:37:07 | [diff] [blame] | 392 | RunCmd(['build/android/buildbot/bb_device_status_check.py'], |
| 393 | halt_on_failure=True) |
[email protected] | 693c54d | 2013-01-09 19:41:25 | [diff] [blame] | 394 | |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 395 | |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 396 | def GetDeviceSetupStepCmds(): |
| 397 | return [ |
[email protected] | f049a19 | 2013-10-10 01:11:26 | [diff] [blame^] | 398 | ('provision_devices', ProvisionDevices), |
| 399 | ('device_status_check', DeviceStatusCheck), |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 400 | ] |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 401 | |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 402 | |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 403 | def RunUnitTests(options): |
| 404 | RunTestSuites(options, gtest_config.STABLE_TEST_SUITES) |
| 405 | |
| 406 | |
| 407 | def RunInstrumentationTests(options): |
| 408 | for test in INSTRUMENTATION_TESTS.itervalues(): |
| 409 | RunInstrumentationSuite(options, test) |
| 410 | |
| 411 | |
| 412 | def RunWebkitTests(options): |
[email protected] | 9e68925 | 2013-07-30 20:14:36 | [diff] [blame] | 413 | RunTestSuites(options, ['webkit_unit_tests']) |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 414 | RunWebkitLint(options.target) |
| 415 | |
| 416 | |
[email protected] | 02bfada | 2013-08-12 05:00:52 | [diff] [blame] | 417 | def RunWebRTCTests(options): |
| 418 | RunTestSuites(options, gtest_config.WEBRTC_TEST_SUITES) |
| 419 | |
| 420 | |
[email protected] | a4b1ec97 | 2013-09-14 05:36:25 | [diff] [blame] | 421 | def RunGPUTests(options): |
| 422 | InstallApk(options, INSTRUMENTATION_TESTS['ContentShell'], False) |
[email protected] | bb508f8 | 2013-09-06 06:42:24 | [diff] [blame] | 423 | bb_annotations.PrintNamedStep('gpu_tests') |
| 424 | RunCmd(['content/test/gpu/run_gpu_test', |
| 425 | '--browser=android-content-shell', 'pixel']) |
| 426 | |
| 427 | |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 428 | def GetTestStepCmds(): |
| 429 | return [ |
| 430 | ('chromedriver', RunChromeDriverTests), |
[email protected] | bb508f8 | 2013-09-06 06:42:24 | [diff] [blame] | 431 | ('gpu', RunGPUTests), |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 432 | ('unit', RunUnitTests), |
| 433 | ('ui', RunInstrumentationTests), |
| 434 | ('webkit', RunWebkitTests), |
[email protected] | 02bfada | 2013-08-12 05:00:52 | [diff] [blame] | 435 | ('webkit_layout', RunWebkitLayoutTests), |
| 436 | ('webrtc', RunWebRTCTests), |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 437 | ] |
| 438 | |
| 439 | |
[email protected] | 4e622cee | 2013-09-17 18:32:12 | [diff] [blame] | 440 | def UploadHTML(options, gs_base_dir, dir_to_upload, link_text, |
| 441 | link_rel_path='index.html', gs_url=GS_URL): |
| 442 | """Uploads directory at |dir_to_upload| to Google Storage and output a link. |
[email protected] | 485fb23 | 2013-08-22 19:56:33 | [diff] [blame] | 443 | |
| 444 | Args: |
| 445 | options: Command line options. |
[email protected] | 4e622cee | 2013-09-17 18:32:12 | [diff] [blame] | 446 | gs_base_dir: The Google Storage base directory (e.g. |
| 447 | 'chromium-code-coverage/java') |
| 448 | dir_to_upload: Absolute path to the directory to be uploaded. |
| 449 | link_text: Link text to be displayed on the step. |
| 450 | link_rel_path: Link path relative to |dir_to_upload|. |
| 451 | gs_url: Google storage URL. |
[email protected] | 485fb23 | 2013-08-22 19:56:33 | [diff] [blame] | 452 | """ |
[email protected] | e7bd341 | 2013-09-20 17:15:28 | [diff] [blame] | 453 | revision = _GetRevision(options) |
[email protected] | 485fb23 | 2013-08-22 19:56:33 | [diff] [blame] | 454 | bot_id = options.build_properties.get('buildername', 'testing') |
| 455 | randhash = hashlib.sha1(str(random.random())).hexdigest() |
[email protected] | 4e622cee | 2013-09-17 18:32:12 | [diff] [blame] | 456 | gs_path = '%s/%s/%s/%s' % (gs_base_dir, bot_id, revision, randhash) |
| 457 | RunCmd([bb_utils.GSUTIL_PATH, 'cp', '-R', dir_to_upload, 'gs://%s' % gs_path]) |
| 458 | bb_annotations.PrintLink(link_text, |
[email protected] | f049a19 | 2013-10-10 01:11:26 | [diff] [blame^] | 459 | '%s/%s/%s' % (gs_url, gs_path, link_rel_path)) |
[email protected] | 485fb23 | 2013-08-22 19:56:33 | [diff] [blame] | 460 | |
| 461 | |
| 462 | def GenerateJavaCoverageReport(options): |
| 463 | """Generates an HTML coverage report using EMMA and uploads it.""" |
| 464 | bb_annotations.PrintNamedStep('java_coverage_report') |
| 465 | |
| 466 | coverage_html = os.path.join(options.coverage_dir, 'coverage_html') |
| 467 | RunCmd(['build/android/generate_emma_html.py', |
| 468 | '--coverage-dir', options.coverage_dir, |
[email protected] | dc8ec3f | 2013-09-07 07:18:14 | [diff] [blame] | 469 | '--metadata-dir', os.path.join(CHROME_OUT_DIR, options.target), |
[email protected] | a1f1abfe | 2013-08-27 22:02:43 | [diff] [blame] | 470 | '--cleanup', |
[email protected] | 485fb23 | 2013-08-22 19:56:33 | [diff] [blame] | 471 | '--output', os.path.join(coverage_html, 'index.html')]) |
[email protected] | 4e622cee | 2013-09-17 18:32:12 | [diff] [blame] | 472 | return coverage_html |
[email protected] | 485fb23 | 2013-08-22 19:56:33 | [diff] [blame] | 473 | |
| 474 | |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 475 | def LogcatDump(options): |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 476 | # Print logcat, kill logcat monitor |
[email protected] | b387389 | 2013-07-10 04:57:10 | [diff] [blame] | 477 | bb_annotations.PrintNamedStep('logcat_dump') |
[email protected] | dc8ec3f | 2013-09-07 07:18:14 | [diff] [blame] | 478 | logcat_file = os.path.join(CHROME_OUT_DIR, options.target, 'full_log') |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 479 | with open(logcat_file, 'w') as f: |
| 480 | RunCmd([ |
[email protected] | dc8ec3f | 2013-09-07 07:18:14 | [diff] [blame] | 481 | os.path.join(CHROME_SRC_DIR, 'build', 'android', |
| 482 | 'adb_logcat_printer.py'), |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 483 | LOGCAT_DIR], stdout=f) |
| 484 | RunCmd(['cat', logcat_file]) |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 485 | |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 486 | |
| 487 | def GenerateTestReport(options): |
[email protected] | b387389 | 2013-07-10 04:57:10 | [diff] [blame] | 488 | bb_annotations.PrintNamedStep('test_report') |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 489 | for report in glob.glob( |
[email protected] | dc8ec3f | 2013-09-07 07:18:14 | [diff] [blame] | 490 | os.path.join(CHROME_OUT_DIR, options.target, 'test_logs', '*.log')): |
[email protected] | 78af871 | 2013-01-14 10:37:12 | [diff] [blame] | 491 | RunCmd(['cat', report]) |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 492 | os.remove(report) |
| 493 | |
| 494 | |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 495 | def MainTestWrapper(options): |
[email protected] | 4cf098c | 2013-08-02 21:08:06 | [diff] [blame] | 496 | try: |
| 497 | # Spawn logcat monitor |
| 498 | SpawnLogcatMonitor() |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 499 | |
[email protected] | 4cf098c | 2013-08-02 21:08:06 | [diff] [blame] | 500 | # Run all device setup steps |
| 501 | for _, cmd in GetDeviceSetupStepCmds(): |
| 502 | cmd(options) |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 503 | |
[email protected] | 4cf098c | 2013-08-02 21:08:06 | [diff] [blame] | 504 | if options.install: |
| 505 | test_obj = INSTRUMENTATION_TESTS[options.install] |
| 506 | InstallApk(options, test_obj, print_step=True) |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 507 | |
[email protected] | 4cf098c | 2013-08-02 21:08:06 | [diff] [blame] | 508 | if options.test_filter: |
| 509 | bb_utils.RunSteps(options.test_filter, GetTestStepCmds(), options) |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 510 | |
[email protected] | 485fb23 | 2013-08-22 19:56:33 | [diff] [blame] | 511 | if options.coverage_bucket: |
[email protected] | 4e622cee | 2013-09-17 18:32:12 | [diff] [blame] | 512 | coverage_html = GenerateJavaCoverageReport(options) |
| 513 | UploadHTML(options, '%s/java' % options.coverage_bucket, coverage_html, |
| 514 | 'Coverage Report') |
[email protected] | 485fb23 | 2013-08-22 19:56:33 | [diff] [blame] | 515 | |
[email protected] | 4cf098c | 2013-08-02 21:08:06 | [diff] [blame] | 516 | if options.experimental: |
| 517 | RunTestSuites(options, gtest_config.EXPERIMENTAL_TEST_SUITES) |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 518 | |
[email protected] | 4cf098c | 2013-08-02 21:08:06 | [diff] [blame] | 519 | finally: |
| 520 | # Run all post test steps |
| 521 | LogcatDump(options) |
| 522 | GenerateTestReport(options) |
| 523 | # KillHostHeartbeat() has logic to check if heartbeat process is running, |
| 524 | # and kills only if it finds the process is running on the host. |
| 525 | provision_devices.KillHostHeartbeat() |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 526 | |
| 527 | |
| 528 | def GetDeviceStepsOptParser(): |
[email protected] | c528275 | 2013-06-07 23:14:39 | [diff] [blame] | 529 | parser = bb_utils.GetParser() |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 530 | parser.add_option('--experimental', action='store_true', |
| 531 | help='Run experiemental tests') |
| 532 | parser.add_option('-f', '--test-filter', metavar='<filter>', default=[], |
| 533 | action='append', |
| 534 | help=('Run a test suite. Test suites: "%s"' % |
| 535 | '", "'.join(VALID_TESTS))) |
| 536 | parser.add_option('--asan', action='store_true', help='Run tests with asan.') |
| 537 | parser.add_option('--install', metavar='<apk name>', |
| 538 | help='Install an apk by name') |
[email protected] | 78af871 | 2013-01-14 10:37:12 | [diff] [blame] | 539 | parser.add_option('--reboot', action='store_true', |
| 540 | help='Reboot devices before running tests') |
[email protected] | 485fb23 | 2013-08-22 19:56:33 | [diff] [blame] | 541 | parser.add_option('--coverage-bucket', |
| 542 | help=('Bucket name to store coverage results. Coverage is ' |
| 543 | 'only run if this is set.')) |
[email protected] | 14f139f4 | 2013-07-24 18:41:58 | [diff] [blame] | 544 | parser.add_option( |
| 545 | '--flakiness-server', |
[email protected] | f049a19 | 2013-10-10 01:11:26 | [diff] [blame^] | 546 | help=('The flakiness dashboard server to which the results should be ' |
| 547 | 'uploaded.')) |
[email protected] | 12f36c8 | 2013-03-29 06:21:13 | [diff] [blame] | 548 | parser.add_option( |
| 549 | '--auto-reconnect', action='store_true', |
| 550 | help='Push script to device which restarts adbd on disconnections.') |
[email protected] | 7405027 | 2013-07-02 14:02:15 | [diff] [blame] | 551 | parser.add_option( |
| 552 | '--logcat-dump-output', |
| 553 | help='The logcat dump output will be "tee"-ed into this file') |
| 554 | |
| 555 | return parser |
| 556 | |
| 557 | |
| 558 | def main(argv): |
| 559 | parser = GetDeviceStepsOptParser() |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 560 | options, args = parser.parse_args(argv[1:]) |
| 561 | |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 562 | if args: |
[email protected] | c528275 | 2013-06-07 23:14:39 | [diff] [blame] | 563 | return sys.exit('Unused args %s' % args) |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 564 | |
| 565 | unknown_tests = set(options.test_filter) - VALID_TESTS |
| 566 | if unknown_tests: |
[email protected] | c528275 | 2013-06-07 23:14:39 | [diff] [blame] | 567 | return sys.exit('Unknown tests %s' % list(unknown_tests)) |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 568 | |
| 569 | setattr(options, 'target', options.factory_properties.get('target', 'Debug')) |
[email protected] | 485fb23 | 2013-08-22 19:56:33 | [diff] [blame] | 570 | if options.coverage_bucket: |
| 571 | setattr(options, 'coverage_dir', |
[email protected] | dc8ec3f | 2013-09-07 07:18:14 | [diff] [blame] | 572 | os.path.join(CHROME_OUT_DIR, options.target, 'coverage')) |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 573 | |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 574 | MainTestWrapper(options) |
[email protected] | e185b7e8 | 2013-01-09 03:49:57 | [diff] [blame] | 575 | |
| 576 | |
| 577 | if __name__ == '__main__': |
| 578 | sys.exit(main(sys.argv)) |