| 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 | |
| 9 import multiprocessing | 8 import multiprocessing |
| 10 import optparse | |
| 11 import os | 9 import os |
| 12 import pipes | |
| 13 import shutil | 10 import shutil |
| 14 import subprocess | |
| 15 import sys | 11 import sys |
| 16 | 12 |
| 13 import bb_utils |
| 14 |
| 17 sys.path.append(os.path.join(os.path.dirname(__file__), '..')) | 15 sys.path.append(os.path.join(os.path.dirname(__file__), '..')) |
| 18 from pylib import android_commands | 16 from pylib import android_commands |
| 19 from pylib import buildbot_report | 17 from pylib import buildbot_report |
| 20 from pylib import constants | 18 from pylib import constants |
| 21 from pylib.gtest import gtest_config | 19 from pylib.gtest import gtest_config |
| 22 | 20 |
| 23 sys.path.append(os.path.join( | 21 sys.path.append(os.path.join( |
| 24 constants.DIR_SOURCE_ROOT, 'third_party', 'android_testrunner')) | 22 constants.DIR_SOURCE_ROOT, 'third_party', 'android_testrunner')) |
| 25 import errors | 23 import errors |
| 26 | 24 |
| 27 | 25 |
| 28 TESTING = 'BUILDBOT_TESTING' in os.environ | |
| 29 | |
| 30 CHROME_SRC = constants.DIR_SOURCE_ROOT | 26 CHROME_SRC = constants.DIR_SOURCE_ROOT |
| 31 | 27 |
| 32 # Describes an instrumation test suite: | 28 # Describes an instrumation test suite: |
| 33 # test: Name of test we're running. | 29 # test: Name of test we're running. |
| 34 # apk: apk to be installed. | 30 # apk: apk to be installed. |
| 35 # apk_package: package for the apk to be installed. | 31 # apk_package: package for the apk to be installed. |
| 36 # test_apk: apk to run tests on. | 32 # test_apk: apk to run tests on. |
| 37 # test_data: data folder in format destination:source. | 33 # test_data: data folder in format destination:source. |
| 38 I_TEST = collections.namedtuple('InstrumentationTest', [ | 34 I_TEST = collections.namedtuple('InstrumentationTest', [ |
| 39 'name', 'apk', 'apk_package', 'test_apk', 'test_data', 'host_driven_root']) | 35 'name', 'apk', 'apk_package', 'test_apk', 'test_data', 'host_driven_root']) |
| (...skipping 14 matching lines...) Expand all Loading... |
| 54 I_TEST('AndroidWebView', | 50 I_TEST('AndroidWebView', |
| 55 'AndroidWebView.apk', | 51 'AndroidWebView.apk', |
| 56 'org.chromium.android_webview.shell', | 52 'org.chromium.android_webview.shell', |
| 57 'AndroidWebViewTest', | 53 'AndroidWebViewTest', |
| 58 'webview:android_webview/test/data/device_files', | 54 'webview:android_webview/test/data/device_files', |
| 59 None), | 55 None), |
| 60 ]) | 56 ]) |
| 61 | 57 |
| 62 VALID_TESTS = set(['chromedriver', 'ui', 'unit', 'webkit', 'webkit_layout']) | 58 VALID_TESTS = set(['chromedriver', 'ui', 'unit', 'webkit', 'webkit_layout']) |
| 63 | 59 |
| 64 | 60 RunCmd = bb_utils.RunCmd |
| 65 def SpawnCmd(command): | |
| 66 """Spawn a process without waiting for termination.""" | |
| 67 print '>', ' '.join(map(pipes.quote, command)) | |
| 68 sys.stdout.flush() | |
| 69 if TESTING: | |
| 70 class MockPopen(object): | |
| 71 @staticmethod | |
| 72 def wait(): | |
| 73 return 0 | |
| 74 return MockPopen() | |
| 75 | |
| 76 return subprocess.Popen(command, cwd=CHROME_SRC) | |
| 77 | |
| 78 def RunCmd(command, flunk_on_failure=True, halt_on_failure=False): | |
| 79 """Run a command relative to the chrome source root.""" | |
| 80 code = SpawnCmd(command).wait() | |
| 81 print '<', ' '.join(map(pipes.quote, command)) | |
| 82 if code != 0: | |
| 83 print 'ERROR: process exited with code %d' % code | |
| 84 if flunk_on_failure: | |
| 85 buildbot_report.PrintError() | |
| 86 else: | |
| 87 buildbot_report.PrintWarning() | |
| 88 # Allow steps to have both halting (i.e. 1) and non-halting exit codes. | |
| 89 if code != 0 and code != 88 and halt_on_failure: | |
| 90 raise OSError() | |
| 91 return code | |
| 92 | 61 |
| 93 | 62 |
| 94 # multiprocessing map_async requires a top-level function for pickle library. | 63 # multiprocessing map_async requires a top-level function for pickle library. |
| 95 def RebootDeviceSafe(device): | 64 def RebootDeviceSafe(device): |
| 96 """Reboot a device, wait for it to start, and squelch timeout exceptions.""" | 65 """Reboot a device, wait for it to start, and squelch timeout exceptions.""" |
| 97 try: | 66 try: |
| 98 android_commands.AndroidCommands(device).Reboot(True) | 67 android_commands.AndroidCommands(device).Reboot(True) |
| 99 except errors.DeviceUnresponsiveError as e: | 68 except errors.DeviceUnresponsiveError as e: |
| 100 return e | 69 return e |
| 101 | 70 |
| 102 | 71 |
| 103 def RebootDevices(): | 72 def RebootDevices(): |
| 104 """Reboot all attached and online devices.""" | 73 """Reboot all attached and online devices.""" |
| 105 buildbot_report.PrintNamedStep('Reboot devices') | 74 buildbot_report.PrintNamedStep('Reboot devices') |
| 106 # Early return here to avoid presubmit dependence on adb, | 75 # Early return here to avoid presubmit dependence on adb, |
| 107 # which might not exist in this checkout. | 76 # which might not exist in this checkout. |
| 108 if TESTING: | 77 if bb_utils.TESTING: |
| 109 return | 78 return |
| 110 devices = android_commands.GetAttachedDevices() | 79 devices = android_commands.GetAttachedDevices() |
| 111 print 'Rebooting: %s' % devices | 80 print 'Rebooting: %s' % devices |
| 112 if devices: | 81 if devices: |
| 113 pool = multiprocessing.Pool(len(devices)) | 82 pool = multiprocessing.Pool(len(devices)) |
| 114 results = pool.map_async(RebootDeviceSafe, devices).get(99999) | 83 results = pool.map_async(RebootDeviceSafe, devices).get(99999) |
| 115 | 84 |
| 116 for device, result in zip(devices, results): | 85 for device, result in zip(devices, results): |
| 117 if result: | 86 if result: |
| 118 print '%s failed to startup.' % device | 87 print '%s failed to startup.' % device |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 253 | 222 |
| 254 def MainTestWrapper(options): | 223 def MainTestWrapper(options): |
| 255 # Restart adb to work around bugs, sleep to wait for usb discovery. | 224 # Restart adb to work around bugs, sleep to wait for usb discovery. |
| 256 RunCmd(['adb', 'kill-server']) | 225 RunCmd(['adb', 'kill-server']) |
| 257 RunCmd(['adb', 'start-server']) | 226 RunCmd(['adb', 'start-server']) |
| 258 RunCmd(['sleep', '1']) | 227 RunCmd(['sleep', '1']) |
| 259 | 228 |
| 260 # Spawn logcat monitor | 229 # Spawn logcat monitor |
| 261 logcat_dir = os.path.join(CHROME_SRC, 'out/logcat') | 230 logcat_dir = os.path.join(CHROME_SRC, 'out/logcat') |
| 262 shutil.rmtree(logcat_dir, ignore_errors=True) | 231 shutil.rmtree(logcat_dir, ignore_errors=True) |
| 263 SpawnCmd(['build/android/adb_logcat_monitor.py', logcat_dir]) | 232 bb_utils.SpawnCmd(['build/android/adb_logcat_monitor.py', logcat_dir]) |
| 264 | 233 |
| 265 # Wait for logcat_monitor to pull existing logcat | 234 # Wait for logcat_monitor to pull existing logcat |
| 266 RunCmd(['sleep', '5']) | 235 RunCmd(['sleep', '5']) |
| 267 | 236 |
| 268 if options.reboot: | 237 if options.reboot: |
| 269 RebootDevices() | 238 RebootDevices() |
| 270 | 239 |
| 271 # Device check and alert emails | 240 # Device check and alert emails |
| 272 buildbot_report.PrintNamedStep('device_status_check') | 241 buildbot_report.PrintNamedStep('device_status_check') |
| 273 RunCmd(['build/android/device_status_check.py'], halt_on_failure=True) | 242 RunCmd(['build/android/device_status_check.py'], halt_on_failure=True) |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 305 RunCmd(['build/android/adb_logcat_printer.py', logcat_dir]) | 274 RunCmd(['build/android/adb_logcat_printer.py', logcat_dir]) |
| 306 | 275 |
| 307 buildbot_report.PrintNamedStep('test_report') | 276 buildbot_report.PrintNamedStep('test_report') |
| 308 for report in glob.glob( | 277 for report in glob.glob( |
| 309 os.path.join(CHROME_SRC, 'out', options.target, 'test_logs', '*.log')): | 278 os.path.join(CHROME_SRC, 'out', options.target, 'test_logs', '*.log')): |
| 310 RunCmd(['cat', report]) | 279 RunCmd(['cat', report]) |
| 311 os.remove(report) | 280 os.remove(report) |
| 312 | 281 |
| 313 | 282 |
| 314 def main(argv): | 283 def main(argv): |
| 315 parser = optparse.OptionParser() | 284 parser = bb_utils.GetParser() |
| 316 | |
| 317 def convert_json(option, _, value, parser): | |
| 318 setattr(parser.values, option.dest, json.loads(value)) | |
| 319 | |
| 320 parser.add_option('--build-properties', action='callback', | |
| 321 callback=convert_json, type='string', default={}, | |
| 322 help='build properties in JSON format') | |
| 323 parser.add_option('--factory-properties', action='callback', | |
| 324 callback=convert_json, type='string', default={}, | |
| 325 help='factory properties in JSON format') | |
| 326 parser.add_option('--slave-properties', action='callback', | |
| 327 callback=convert_json, type='string', default={}, | |
| 328 help='Properties set by slave script in JSON format') | |
| 329 parser.add_option('--experimental', action='store_true', | 285 parser.add_option('--experimental', action='store_true', |
| 330 help='Run experiemental tests') | 286 help='Run experiemental tests') |
| 331 parser.add_option('-f', '--test-filter', metavar='<filter>', default=[], | 287 parser.add_option('-f', '--test-filter', metavar='<filter>', default=[], |
| 332 action='append', | 288 action='append', |
| 333 help=('Run a test suite. Test suites: "%s"' % | 289 help=('Run a test suite. Test suites: "%s"' % |
| 334 '", "'.join(VALID_TESTS))) | 290 '", "'.join(VALID_TESTS))) |
| 335 parser.add_option('--asan', action='store_true', help='Run tests with asan.') | 291 parser.add_option('--asan', action='store_true', help='Run tests with asan.') |
| 336 parser.add_option('--install', metavar='<apk name>', | 292 parser.add_option('--install', metavar='<apk name>', |
| 337 help='Install an apk by name') | 293 help='Install an apk by name') |
| 338 parser.add_option('--reboot', action='store_true', | 294 parser.add_option('--reboot', action='store_true', |
| 339 help='Reboot devices before running tests') | 295 help='Reboot devices before running tests') |
| 340 parser.add_option('--upload-to-flakiness-server', action='store_true', | 296 parser.add_option('--upload-to-flakiness-server', action='store_true', |
| 341 help='Upload the results to the flakiness dashboard.') | 297 help='Upload the results to the flakiness dashboard.') |
| 342 parser.add_option( | 298 parser.add_option( |
| 343 '--auto-reconnect', action='store_true', | 299 '--auto-reconnect', action='store_true', |
| 344 help='Push script to device which restarts adbd on disconnections.') | 300 help='Push script to device which restarts adbd on disconnections.') |
| 345 options, args = parser.parse_args(argv[1:]) | 301 options, args = parser.parse_args(argv[1:]) |
| 346 | 302 |
| 347 def ParserError(msg): | |
| 348 """We avoid parser.error because it calls sys.exit.""" | |
| 349 parser.print_help() | |
| 350 print >> sys.stderr, '\nERROR:', msg | |
| 351 return 1 | |
| 352 | |
| 353 if args: | 303 if args: |
| 354 return ParserError('Unused args %s' % args) | 304 return sys.exit('Unused args %s' % args) |
| 355 | 305 |
| 356 unknown_tests = set(options.test_filter) - VALID_TESTS | 306 unknown_tests = set(options.test_filter) - VALID_TESTS |
| 357 if unknown_tests: | 307 if unknown_tests: |
| 358 return ParserError('Unknown tests %s' % list(unknown_tests)) | 308 return sys.exit('Unknown tests %s' % list(unknown_tests)) |
| 359 | 309 |
| 360 setattr(options, 'target', options.factory_properties.get('target', 'Debug')) | 310 setattr(options, 'target', options.factory_properties.get('target', 'Debug')) |
| 361 | 311 |
| 362 # Add adb binary and chromium-source platform-tools to tip of PATH variable. | 312 # Add adb binary and chromium-source platform-tools to tip of PATH variable. |
| 363 android_paths = [os.path.join(constants.ANDROID_SDK_ROOT, 'platform-tools')] | 313 android_paths = [os.path.join(constants.ANDROID_SDK_ROOT, 'platform-tools')] |
| 364 | 314 |
| 365 # Bots checkout chrome in /b/build/slave/<name>/build/src | 315 # Bots checkout chrome in /b/build/slave/<name>/build/src |
| 366 build_internal_android = os.path.abspath(os.path.join( | 316 build_internal_android = os.path.abspath(os.path.join( |
| 367 CHROME_SRC, '..', '..', '..', '..', '..', 'build_internal', 'scripts', | 317 CHROME_SRC, '..', '..', '..', '..', '..', 'build_internal', 'scripts', |
| 368 'slave', 'android')) | 318 'slave', 'android')) |
| 369 if os.path.exists(build_internal_android): | 319 if os.path.exists(build_internal_android): |
| 370 android_paths.insert(0, build_internal_android) | 320 android_paths.insert(0, build_internal_android) |
| 371 os.environ['PATH'] = os.pathsep.join(android_paths + [os.environ['PATH']]) | 321 os.environ['PATH'] = os.pathsep.join(android_paths + [os.environ['PATH']]) |
| 372 | 322 |
| 373 MainTestWrapper(options) | 323 MainTestWrapper(options) |
| 374 | 324 |
| 375 | 325 |
| 376 if __name__ == '__main__': | 326 if __name__ == '__main__': |
| 377 sys.exit(main(sys.argv)) | 327 sys.exit(main(sys.argv)) |
| OLD | NEW |