OLD | NEW |
(Empty) | |
| 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 |
| 8 import json |
| 9 import optparse |
| 10 import os |
| 11 import pipes |
| 12 import shutil |
| 13 import subprocess |
| 14 import sys |
| 15 |
| 16 sys.path.append(os.path.join(os.path.dirname(__file__), '..')) |
| 17 from pylib import buildbot_report |
| 18 from pylib import constants |
| 19 |
| 20 |
| 21 TESTING = 'BUILDBOT_TESTING' in os.environ |
| 22 |
| 23 CHROME_SRC = constants.CHROME_DIR |
| 24 |
| 25 # Describes an instrumation test suite: |
| 26 # test: Name of test we're running. |
| 27 # apk: apk to be installed. |
| 28 # apk_package: package for the apk to be installed. |
| 29 # test_apk: apk to run tests on. |
| 30 # test_data: data folder in format destination:source. |
| 31 I_TEST = collections.namedtuple('InstrumentationTest', [ |
| 32 'name', 'apk', 'apk_package', 'test_apk', 'test_data']) |
| 33 |
| 34 INSTRUMENTATION_TESTS = dict((suite.name, suite) for suite in [ |
| 35 I_TEST('ContentShell', |
| 36 'ContentShell.apk', |
| 37 'org.chromium.content_shell', |
| 38 'ContentShellTest', |
| 39 'content:content/test/data/android/device_files'), |
| 40 I_TEST('ChromiumTestShell', |
| 41 'ChromiumTestShell.apk', |
| 42 'org.chromium.chrome.testshell', |
| 43 'ChromiumTestShellTest', |
| 44 'chrome:chrome/test/data/android/device_files'), |
| 45 I_TEST('AndroidWebView', |
| 46 'AndroidWebView.apk', |
| 47 'org.chromium.android_webview', |
| 48 'AndroidWebViewTest', |
| 49 'webview:android_webview/test/data/device_files'), |
| 50 ]) |
| 51 |
| 52 VALID_TESTS = set(['ui', 'unit', 'webkit', 'webkit_layout']) |
| 53 |
| 54 |
| 55 def RunCmd(command, flunk_on_failure=True): |
| 56 """Run a command relative to the chrome source root.""" |
| 57 |
| 58 # Add adb binary to path. In the future, might use build_internal copy. |
| 59 env = dict(os.environ) |
| 60 env['PATH'] = os.pathsep.join([ |
| 61 env['PATH'], os.path.join(constants.ANDROID_SDK_ROOT, 'platform-tools')]) |
| 62 |
| 63 command_str = ' '.join(map(pipes.quote, command)) |
| 64 print '>', command_str |
| 65 if not TESTING: |
| 66 code = subprocess.Popen(command, cwd=CHROME_SRC, env=env).wait() |
| 67 else: |
| 68 code = 0 |
| 69 print '<', command_str |
| 70 if code != 0: |
| 71 print 'ERROR: non-zero status %d from %s' % (code, command) |
| 72 if flunk_on_failure: |
| 73 buildbot_report.PrintError() |
| 74 else: |
| 75 buildbot_report.PrintWarning() |
| 76 return code |
| 77 |
| 78 |
| 79 def RunTestSuites(options, suite): |
| 80 """Manages an invocation of run_tests.py. |
| 81 |
| 82 Args: |
| 83 options: options object. |
| 84 suite: The suite to pass to run_tests or None to run default suites. |
| 85 """ |
| 86 args = ['--verbose'] |
| 87 if suite: |
| 88 args.extend(['-s', suite]) |
| 89 if options.target == 'Release': |
| 90 args.append('--release') |
| 91 if options.asan: |
| 92 args.append('--tool=asan') |
| 93 RunCmd(['build/android/run_tests.py'] + args) |
| 94 |
| 95 |
| 96 def InstallApk(apk, apk_package, target): |
| 97 args = ['--apk', apk, '--apk_package', apk_package] |
| 98 if target == 'Release': |
| 99 args.append('--release') |
| 100 |
| 101 RunCmd(['build/android/adb_install_apk.py'] + args) |
| 102 |
| 103 |
| 104 def RunInstrumentationSuite(options, test): |
| 105 """Manages an invocation of run_instrumentaiton_tests.py. |
| 106 |
| 107 Args: |
| 108 options: options object |
| 109 test: An I_TEST namedtuple |
| 110 """ |
| 111 buildbot_report.PrintNamedStep('%s_instrumentation_tests' % test.name.lower()) |
| 112 |
| 113 InstallApk(test.apk, test.apk_package, options.target) |
| 114 args = ['--test-apk', test.test_apk, '--test_data', test.test_data, '-vvv', |
| 115 '-I'] |
| 116 if options.target == 'Release': |
| 117 args.append('--release') |
| 118 if options.asan: |
| 119 args.append('--tool=asan') |
| 120 |
| 121 RunCmd(['build/android/run_instrumentation_tests.py'] + args) |
| 122 |
| 123 |
| 124 def RunWebkitLint(target): |
| 125 """Lint WebKit's TestExpectation files.""" |
| 126 buildbot_report.PrintNamedStep('webkit_lint') |
| 127 RunCmd(['webkit/tools/layout_tests/run_webkit_tests.py', |
| 128 '--lint-test-files', |
| 129 '--chromium', |
| 130 '--target', target]) |
| 131 |
| 132 |
| 133 def RunWebkitLayoutTests(options): |
| 134 """Run layout tests on an actual device.""" |
| 135 buildbot_report.PrintNamedStep('webkit_tests') |
| 136 RunCmd(['webkit/tools/layout_tests/run_webkit_tests.py', |
| 137 '--no-show-results', |
| 138 '--no-new-test-results', |
| 139 '--full-results-html', |
| 140 '--clobber-old-results', |
| 141 '--exit-after-n-failures', '5000', |
| 142 '--exit-after-n-crashes-or-timeouts', '100', |
| 143 '--debug-rwt-logging', |
| 144 '--results-directory', '..layout-test-results', |
| 145 '--target', options.target, |
| 146 '--builder-name', options.build_properties.get('buildername', ''), |
| 147 '--build-number', options.build_properties.get('buildnumber', ''), |
| 148 '--master-name', options.build_properties.get('mastername', ''), |
| 149 '--build-name', options.build_properties.get('buildername', ''), |
| 150 '--platform=chromium-android', |
| 151 '--test-results-server', |
| 152 options.factory_properties.get('test_results_server', '')]) |
| 153 |
| 154 |
| 155 def MainTestWrapper(options): |
| 156 if options.install: |
| 157 test_obj = INSTRUMENTATION_TESTS[options.install] |
| 158 InstallApk(test_obj.apk, test_obj.apk_package, options.target) |
| 159 |
| 160 if not options.test_filter: |
| 161 return |
| 162 |
| 163 # Device check and alert emails |
| 164 RunCmd(['build/android/device_status_check.py'], flunk_on_failure=False) |
| 165 |
| 166 # Spawn logcat monitor |
| 167 logcat_dir = os.path.join(CHROME_SRC, 'out/logcat') |
| 168 shutil.rmtree(logcat_dir, ignore_errors=True) |
| 169 if not TESTING: |
| 170 subprocess.Popen( |
| 171 ['build/android/adb_logcat_monitor.py', logcat_dir], cwd=CHROME_SRC) |
| 172 |
| 173 if 'unit' in options.test_filter: |
| 174 RunTestSuites(options, None) |
| 175 if 'ui' in options.test_filter: |
| 176 for test in INSTRUMENTATION_TESTS.itervalues(): |
| 177 RunInstrumentationSuite(options, test) |
| 178 if 'webkit' in options.test_filter: |
| 179 RunTestSuites(options, 'webkit_unit_tests') |
| 180 RunTestSuites(options, 'TestWebKitAPI') |
| 181 RunWebkitLint(options.target) |
| 182 if 'webkit_layout' in options.test_filter: |
| 183 RunWebkitLayoutTests(options) |
| 184 |
| 185 if options.experimental: |
| 186 pass |
| 187 |
| 188 # Print logcat, kill logcat monitor |
| 189 buildbot_report.PrintNamedStep('Logcat dump') |
| 190 RunCmd(['build/android/adb_logcat_printer.py', logcat_dir]) |
| 191 |
| 192 buildbot_report.PrintNamedStep('Test report') |
| 193 for report in glob.glob( |
| 194 os.path.join(CHROME_SRC, 'out', options.target, 'test_logs', '*.log')): |
| 195 subprocess.Popen(['cat', report]).wait() |
| 196 os.remove(report) |
| 197 |
| 198 |
| 199 def main(argv): |
| 200 parser = optparse.OptionParser() |
| 201 |
| 202 def convert_json(option, _, value, parser): |
| 203 setattr(parser.values, option.dest, json.loads(value)) |
| 204 |
| 205 parser.add_option('--build-properties', action='callback', |
| 206 callback=convert_json, type='string', default={}, |
| 207 help='build properties in JSON format') |
| 208 parser.add_option('--factory-properties', action='callback', |
| 209 callback=convert_json, type='string', default={}, |
| 210 help='factory properties in JSON format') |
| 211 parser.add_option('--slave-properties', action='callback', |
| 212 callback=convert_json, type='string', default={}, |
| 213 help='Properties set by slave script in JSON format') |
| 214 parser.add_option('--experimental', action='store_true', |
| 215 help='Run experiemental tests') |
| 216 parser.add_option('-f', '--test-filter', metavar='<filter>', default=[], |
| 217 action='append', |
| 218 help=('Run a test suite. Test suites: "%s"' % |
| 219 '", "'.join(VALID_TESTS))) |
| 220 parser.add_option('--asan', action='store_true', help='Run tests with asan.') |
| 221 parser.add_option('--install', metavar='<apk name>', |
| 222 help='Install an apk by name') |
| 223 options, args = parser.parse_args(argv[1:]) |
| 224 |
| 225 def ParserError(msg): |
| 226 """We avoid parser.error because it calls sys.exit.""" |
| 227 parser.print_help() |
| 228 print >> sys.stderr, '\nERROR:', msg |
| 229 return 1 |
| 230 |
| 231 if args: |
| 232 return ParserError('Unused args %s' % args) |
| 233 |
| 234 unknown_tests = set(options.test_filter) - VALID_TESTS |
| 235 if unknown_tests: |
| 236 return ParserError('Unknown tests %s' % list(unknown_tests)) |
| 237 |
| 238 setattr(options, 'target', options.factory_properties.get('target', 'Debug')) |
| 239 |
| 240 MainTestWrapper(options) |
| 241 |
| 242 |
| 243 if __name__ == '__main__': |
| 244 sys.exit(main(sys.argv)) |
OLD | NEW |