OLD | NEW |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | 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 | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 import collections | 6 import collections |
7 import glob | 7 import glob |
8 import json | 8 import json |
9 import multiprocessing | |
9 import optparse | 10 import optparse |
10 import os | 11 import os |
11 import pipes | 12 import pipes |
12 import shutil | 13 import shutil |
13 import subprocess | 14 import subprocess |
14 import sys | 15 import sys |
15 | 16 |
16 sys.path.append(os.path.join(os.path.dirname(__file__), '..')) | 17 sys.path.append(os.path.join(os.path.dirname(__file__), '..')) |
18 from pylib import android_commands | |
17 from pylib import buildbot_report | 19 from pylib import buildbot_report |
18 from pylib import constants | 20 from pylib import constants |
19 from pylib.gtest import gtest_config | 21 from pylib.gtest import gtest_config |
20 | 22 |
23 sys.path.append(os.path.join( | |
24 constants.CHROME_DIR, 'third_party', 'android_testrunner')) | |
25 import errors | |
26 | |
21 | 27 |
22 TESTING = 'BUILDBOT_TESTING' in os.environ | 28 TESTING = 'BUILDBOT_TESTING' in os.environ |
23 | 29 |
24 CHROME_SRC = constants.CHROME_DIR | 30 CHROME_SRC = constants.CHROME_DIR |
25 | 31 |
26 # Describes an instrumation test suite: | 32 # Describes an instrumation test suite: |
27 # test: Name of test we're running. | 33 # test: Name of test we're running. |
28 # apk: apk to be installed. | 34 # apk: apk to be installed. |
29 # apk_package: package for the apk to be installed. | 35 # apk_package: package for the apk to be installed. |
30 # test_apk: apk to run tests on. | 36 # test_apk: apk to run tests on. |
(...skipping 15 matching lines...) Expand all Loading... | |
46 I_TEST('AndroidWebView', | 52 I_TEST('AndroidWebView', |
47 'AndroidWebView.apk', | 53 'AndroidWebView.apk', |
48 'org.chromium.android_webview', | 54 'org.chromium.android_webview', |
49 'AndroidWebViewTest', | 55 'AndroidWebViewTest', |
50 'webview:android_webview/test/data/device_files'), | 56 'webview:android_webview/test/data/device_files'), |
51 ]) | 57 ]) |
52 | 58 |
53 VALID_TESTS = set(['ui', 'unit', 'webkit', 'webkit_layout']) | 59 VALID_TESTS = set(['ui', 'unit', 'webkit', 'webkit_layout']) |
54 | 60 |
55 | 61 |
62 | |
56 def SpawnCmd(command): | 63 def SpawnCmd(command): |
57 """Spawn a process without waiting for termination.""" | 64 """Spawn a process without waiting for termination.""" |
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 print '>', ' '.join(map(pipes.quote, command)) | 65 print '>', ' '.join(map(pipes.quote, command)) |
63 sys.stdout.flush() | 66 sys.stdout.flush() |
64 if TESTING: | 67 if TESTING: |
65 class MockPopen(object): | 68 class MockPopen(object): |
66 @staticmethod | 69 @staticmethod |
67 def wait(): | 70 def wait(): |
68 return 0 | 71 return 0 |
69 return MockPopen() | 72 return MockPopen() |
70 | 73 |
71 return subprocess.Popen(command, cwd=CHROME_SRC, env=env) | 74 return subprocess.Popen(command, cwd=CHROME_SRC) |
72 | 75 |
73 def RunCmd(command, flunk_on_failure=True): | 76 def RunCmd(command, flunk_on_failure=True): |
74 """Run a command relative to the chrome source root.""" | 77 """Run a command relative to the chrome source root.""" |
75 code = SpawnCmd(command).wait() | 78 code = SpawnCmd(command).wait() |
76 print '<', ' '.join(map(pipes.quote, command)) | 79 print '<', ' '.join(map(pipes.quote, command)) |
77 if code != 0: | 80 if code != 0: |
78 print 'ERROR: non-zero status %d from %s' % (code, command) | 81 print 'ERROR: process exited with code %d' % code |
79 if flunk_on_failure: | 82 if flunk_on_failure: |
80 buildbot_report.PrintError() | 83 buildbot_report.PrintError() |
81 else: | 84 else: |
82 buildbot_report.PrintWarning() | 85 buildbot_report.PrintWarning() |
83 return code | 86 return code |
84 | 87 |
85 | 88 |
89 # This has to be a static function due to multiprocessing pickiness | |
bulach
2013/01/14 09:44:43
nit: for future readers, how about:
# Multiproces
Isaac (away)
2013/01/14 10:37:28
Done.
| |
90 def RebootDeviceSafe(device): | |
91 """Reboot a device, wait for it to start, and squelch timeout exceptions.""" | |
92 try: | |
93 android_commands.AndroidCommands(device).Reboot(True) | |
94 except errors.DeviceUnresponsiveError as e: | |
95 return e | |
96 | |
97 | |
98 def RebootDevices(): | |
99 """Reboot all attached and online devices.""" | |
100 buildbot_report.PrintNamedStep('Reboot devices') | |
101 devices = android_commands.GetAttachedDevices() | |
102 print 'Rebooting: %s' % devices | |
103 if devices and not TESTING: | |
104 pool = multiprocessing.Pool(len(devices)) | |
105 results = pool.map_async(RebootDeviceSafe, devices).get(99999) | |
106 | |
107 for device, result in zip(devices, results): | |
108 if result: | |
109 print '%s failed to startup.' % device | |
110 | |
111 if any(results): | |
112 buildbot_report.PrintWarning() | |
113 else: | |
114 print 'Reboots complete.' | |
115 | |
116 | |
86 def RunTestSuites(options, suites): | 117 def RunTestSuites(options, suites): |
87 """Manages an invocation of run_tests.py. | 118 """Manages an invocation of run_tests.py. |
88 | 119 |
89 Args: | 120 Args: |
90 options: options object. | 121 options: options object. |
91 suites: List of suites to run. | 122 suites: List of suites to run. |
92 """ | 123 """ |
93 args = ['--verbose'] | 124 args = ['--verbose'] |
94 if options.target == 'Release': | 125 if options.target == 'Release': |
95 args.append('--release') | 126 args.append('--release') |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
162 '--builder-name', options.build_properties.get('buildername', ''), | 193 '--builder-name', options.build_properties.get('buildername', ''), |
163 '--build-number', options.build_properties.get('buildnumber', ''), | 194 '--build-number', options.build_properties.get('buildnumber', ''), |
164 '--master-name', options.build_properties.get('mastername', ''), | 195 '--master-name', options.build_properties.get('mastername', ''), |
165 '--build-name', options.build_properties.get('buildername', ''), | 196 '--build-name', options.build_properties.get('buildername', ''), |
166 '--platform=chromium-android', | 197 '--platform=chromium-android', |
167 '--test-results-server', | 198 '--test-results-server', |
168 options.factory_properties.get('test_results_server', '')]) | 199 options.factory_properties.get('test_results_server', '')]) |
169 | 200 |
170 | 201 |
171 def MainTestWrapper(options): | 202 def MainTestWrapper(options): |
203 # Restart adb to work around bugs, sleep to wait for usb discovery. | |
204 RunCmd(['adb', 'kill-server']) | |
205 RunCmd(['adb', 'start-server']) | |
206 RunCmd(['sleep', '1']) | |
207 | |
208 # Spawn logcat monitor | |
209 logcat_dir = os.path.join(CHROME_SRC, 'out/logcat') | |
210 shutil.rmtree(logcat_dir, ignore_errors=True) | |
211 SpawnCmd(['build/android/adb_logcat_monitor.py', logcat_dir]) | |
212 | |
213 # Wait for logcat_monitor to pull existing logcat | |
214 RunCmd(['sleep', '5']) | |
215 | |
216 if options.reboot: | |
217 RebootDevices() | |
218 | |
172 # Device check and alert emails | 219 # Device check and alert emails |
173 buildbot_report.PrintNamedStep('device_status_check') | 220 buildbot_report.PrintNamedStep('device_status_check') |
174 RunCmd(['build/android/device_status_check.py'], flunk_on_failure=False) | 221 RunCmd(['build/android/device_status_check.py'], flunk_on_failure=False) |
175 | 222 |
176 if options.install: | 223 if options.install: |
177 test_obj = INSTRUMENTATION_TESTS[options.install] | 224 test_obj = INSTRUMENTATION_TESTS[options.install] |
178 InstallApk(options, test_obj, print_step=True) | 225 InstallApk(options, test_obj, print_step=True) |
179 | 226 |
180 if not options.test_filter: | |
181 return | |
182 | |
183 # Spawn logcat monitor | |
184 logcat_dir = os.path.join(CHROME_SRC, 'out/logcat') | |
185 shutil.rmtree(logcat_dir, ignore_errors=True) | |
186 SpawnCmd(['build/android/adb_logcat_monitor.py', logcat_dir]) | |
187 | |
188 if 'unit' in options.test_filter: | 227 if 'unit' in options.test_filter: |
189 RunTestSuites(options, gtest_config.STABLE_TEST_SUITES) | 228 RunTestSuites(options, gtest_config.STABLE_TEST_SUITES) |
190 if 'ui' in options.test_filter: | 229 if 'ui' in options.test_filter: |
191 for test in INSTRUMENTATION_TESTS.itervalues(): | 230 for test in INSTRUMENTATION_TESTS.itervalues(): |
192 RunInstrumentationSuite(options, test) | 231 RunInstrumentationSuite(options, test) |
193 if 'webkit' in options.test_filter: | 232 if 'webkit' in options.test_filter: |
194 RunTestSuites(options, ['webkit_unit_tests', 'TestWebKitAPI']) | 233 RunTestSuites(options, ['webkit_unit_tests', 'TestWebKitAPI']) |
195 RunWebkitLint(options.target) | 234 RunWebkitLint(options.target) |
196 if 'webkit_layout' in options.test_filter: | 235 if 'webkit_layout' in options.test_filter: |
197 RunWebkitLayoutTests(options) | 236 RunWebkitLayoutTests(options) |
198 | 237 |
199 if options.experimental: | 238 if options.experimental: |
200 RunTestSuites(options, gtest_config.EXPERIMENTAL_TEST_SUITES) | 239 RunTestSuites(options, gtest_config.EXPERIMENTAL_TEST_SUITES) |
201 | 240 |
202 # Print logcat, kill logcat monitor | 241 # Print logcat, kill logcat monitor |
203 buildbot_report.PrintNamedStep('logcat_dump') | 242 buildbot_report.PrintNamedStep('logcat_dump') |
204 RunCmd(['build/android/adb_logcat_printer.py', logcat_dir]) | 243 RunCmd(['build/android/adb_logcat_printer.py', logcat_dir]) |
205 | 244 |
206 buildbot_report.PrintNamedStep('test_report') | 245 buildbot_report.PrintNamedStep('test_report') |
207 for report in glob.glob( | 246 for report in glob.glob( |
208 os.path.join(CHROME_SRC, 'out', options.target, 'test_logs', '*.log')): | 247 os.path.join(CHROME_SRC, 'out', options.target, 'test_logs', '*.log')): |
209 subprocess.Popen(['cat', report]).wait() | 248 RunCmd(['cat', report]) |
210 os.remove(report) | 249 os.remove(report) |
211 | 250 |
212 | 251 |
213 def main(argv): | 252 def main(argv): |
214 parser = optparse.OptionParser() | 253 parser = optparse.OptionParser() |
215 | 254 |
216 def convert_json(option, _, value, parser): | 255 def convert_json(option, _, value, parser): |
217 setattr(parser.values, option.dest, json.loads(value)) | 256 setattr(parser.values, option.dest, json.loads(value)) |
218 | 257 |
219 parser.add_option('--build-properties', action='callback', | 258 parser.add_option('--build-properties', action='callback', |
220 callback=convert_json, type='string', default={}, | 259 callback=convert_json, type='string', default={}, |
221 help='build properties in JSON format') | 260 help='build properties in JSON format') |
222 parser.add_option('--factory-properties', action='callback', | 261 parser.add_option('--factory-properties', action='callback', |
223 callback=convert_json, type='string', default={}, | 262 callback=convert_json, type='string', default={}, |
224 help='factory properties in JSON format') | 263 help='factory properties in JSON format') |
225 parser.add_option('--slave-properties', action='callback', | 264 parser.add_option('--slave-properties', action='callback', |
226 callback=convert_json, type='string', default={}, | 265 callback=convert_json, type='string', default={}, |
227 help='Properties set by slave script in JSON format') | 266 help='Properties set by slave script in JSON format') |
228 parser.add_option('--experimental', action='store_true', | 267 parser.add_option('--experimental', action='store_true', |
229 help='Run experiemental tests') | 268 help='Run experiemental tests') |
230 parser.add_option('-f', '--test-filter', metavar='<filter>', default=[], | 269 parser.add_option('-f', '--test-filter', metavar='<filter>', default=[], |
231 action='append', | 270 action='append', |
232 help=('Run a test suite. Test suites: "%s"' % | 271 help=('Run a test suite. Test suites: "%s"' % |
233 '", "'.join(VALID_TESTS))) | 272 '", "'.join(VALID_TESTS))) |
234 parser.add_option('--asan', action='store_true', help='Run tests with asan.') | 273 parser.add_option('--asan', action='store_true', help='Run tests with asan.') |
235 parser.add_option('--install', metavar='<apk name>', | 274 parser.add_option('--install', metavar='<apk name>', |
236 help='Install an apk by name') | 275 help='Install an apk by name') |
276 parser.add_option('--reboot', action='store_true', | |
277 help='Reboot devices before running tests') | |
237 options, args = parser.parse_args(argv[1:]) | 278 options, args = parser.parse_args(argv[1:]) |
238 | 279 |
239 def ParserError(msg): | 280 def ParserError(msg): |
240 """We avoid parser.error because it calls sys.exit.""" | 281 """We avoid parser.error because it calls sys.exit.""" |
241 parser.print_help() | 282 parser.print_help() |
242 print >> sys.stderr, '\nERROR:', msg | 283 print >> sys.stderr, '\nERROR:', msg |
243 return 1 | 284 return 1 |
244 | 285 |
245 if args: | 286 if args: |
246 return ParserError('Unused args %s' % args) | 287 return ParserError('Unused args %s' % args) |
247 | 288 |
248 unknown_tests = set(options.test_filter) - VALID_TESTS | 289 unknown_tests = set(options.test_filter) - VALID_TESTS |
249 if unknown_tests: | 290 if unknown_tests: |
250 return ParserError('Unknown tests %s' % list(unknown_tests)) | 291 return ParserError('Unknown tests %s' % list(unknown_tests)) |
251 | 292 |
252 setattr(options, 'target', options.factory_properties.get('target', 'Debug')) | 293 setattr(options, 'target', options.factory_properties.get('target', 'Debug')) |
253 | 294 |
295 # Add adb binary and chromium-source platform-tools to tip of PATH variable. | |
296 android_paths = [os.path.join(constants.ANDROID_SDK_ROOT, 'platform-tools')] | |
297 | |
298 # Bots checkout chrome in /b/build/slave/<name>/build/src | |
299 build_internal_android = os.path.abspath(os.path.join( | |
300 CHROME_SRC, '..', '..', '..', '..', '..', 'build_internal', 'scripts', | |
301 'slave', 'android')) | |
302 if os.path.exists(build_internal_android): | |
303 android_paths.insert(0, build_internal_android) | |
304 os.environ['PATH'] = os.pathsep.join(android_paths + [os.environ['PATH']]) | |
305 | |
254 MainTestWrapper(options) | 306 MainTestWrapper(options) |
255 | 307 |
256 | 308 |
257 if __name__ == '__main__': | 309 if __name__ == '__main__': |
258 sys.exit(main(sys.argv)) | 310 sys.exit(main(sys.argv)) |
OLD | NEW |