Index: build/android/buildbot/bb_device_steps.py |
diff --git a/build/android/buildbot/bb_device_steps.py b/build/android/buildbot/bb_device_steps.py |
index 64f9e09218144fbd8647ede7574d920fb09b7498..dabe4b5f0c6a33b6fa184ddc8701b1d5f28ca875 100755 |
--- a/build/android/buildbot/bb_device_steps.py |
+++ b/build/android/buildbot/bb_device_steps.py |
@@ -6,6 +6,7 @@ |
import collections |
import glob |
import hashlib |
+import json |
import multiprocessing |
import os |
import random |
@@ -46,6 +47,7 @@ I_TEST = collections.namedtuple('InstrumentationTest', [ |
'name', 'apk', 'apk_package', 'test_apk', 'test_data', 'host_driven_root', |
'annotation', 'exclude_annotation', 'extra_flags']) |
+ |
def I(name, apk, apk_package, test_apk, test_data, host_driven_root=None, |
annotation=None, exclude_annotation=None, extra_flags=None): |
return I_TEST(name, apk, apk_package, test_apk, test_data, host_driven_root, |
@@ -141,17 +143,19 @@ def RunTestSuites(options, suites): |
cmd.append('--num_retries=1') |
RunCmd(cmd) |
+ |
def RunChromeDriverTests(options): |
"""Run all the steps for running chromedriver tests.""" |
bb_annotations.PrintNamedStep('chromedriver_annotation') |
RunCmd(['chrome/test/chromedriver/run_buildbot_steps.py', |
'--android-packages=%s,%s,%s' % |
- (constants.PACKAGE_INFO['chromium_test_shell'].package, |
- constants.PACKAGE_INFO['chrome_stable'].package, |
- constants.PACKAGE_INFO['chrome_beta'].package), |
+ (constants.PACKAGE_INFO['chromium_test_shell'].package, |
+ constants.PACKAGE_INFO['chrome_stable'].package, |
+ constants.PACKAGE_INFO['chrome_beta'].package), |
'--revision=%s' % _GetRevision(options), |
'--update-log']) |
+ |
def InstallApk(options, test, print_step=False): |
"""Install an apk to all phones. |
@@ -227,20 +231,20 @@ def RunWebkitLayoutTests(options): |
"""Run layout tests on an actual device.""" |
bb_annotations.PrintNamedStep('webkit_tests') |
cmd_args = [ |
- '--no-show-results', |
- '--no-new-test-results', |
- '--full-results-html', |
- '--clobber-old-results', |
- '--exit-after-n-failures', '5000', |
- '--exit-after-n-crashes-or-timeouts', '100', |
- '--debug-rwt-logging', |
- '--results-directory', '../layout-test-results', |
- '--target', options.target, |
- '--builder-name', options.build_properties.get('buildername', ''), |
- '--build-number', str(options.build_properties.get('buildnumber', '')), |
- '--master-name', 'ChromiumWebkit', # TODO: Get this from the cfg. |
- '--build-name', options.build_properties.get('buildername', ''), |
- '--platform=android'] |
+ '--no-show-results', |
+ '--no-new-test-results', |
+ '--full-results-html', |
+ '--clobber-old-results', |
+ '--exit-after-n-failures', '5000', |
+ '--exit-after-n-crashes-or-timeouts', '100', |
+ '--debug-rwt-logging', |
+ '--results-directory', '../layout-test-results', |
+ '--target', options.target, |
+ '--builder-name', options.build_properties.get('buildername', ''), |
+ '--build-number', str(options.build_properties.get('buildnumber', '')), |
+ '--master-name', 'ChromiumWebkit', # TODO: Get this from the cfg. |
+ '--build-name', options.build_properties.get('buildername', ''), |
+ '--platform=android'] |
for flag in 'test_results_server', 'driver_name', 'additional_drt_flag': |
if flag in options.factory_properties: |
@@ -257,26 +261,99 @@ def RunWebkitLayoutTests(options): |
cmd_args.extend( |
['--additional-expectations=%s' % os.path.join(CHROME_SRC_DIR, *f)]) |
- RunCmd(['webkit/tools/layout_tests/run_webkit_tests.py'] + cmd_args) |
+ exit_code = RunCmd(['webkit/tools/layout_tests/run_webkit_tests.py'] + |
+ cmd_args) |
+ if exit_code == 254: # AKA -1, internal error. |
+ bb_annotations.PrintMsg('?? (crashed or hung)') |
+ else: |
+ full_results_path = os.path.join('..', 'layout-test-results', |
+ 'full_results.json') |
+ if os.path.exists(full_results_path): |
+ full_results = json.load(open(full_results_path)) |
+ unexpected_failures, unexpected_flakes, unexpected_passes = ( |
+ _ParseLayoutTestResults(full_results)) |
+ if unexpected_failures: |
+ _PrintDashboardLink('failed', unexpected_failures, |
+ max_tests=25) |
+ elif unexpected_passes: |
+ _PrintDashboardLink('unexpected passes', unexpected_passes, |
+ max_tests=10) |
+ if unexpected_flakes: |
+ _PrintDashboardLink('unexpected flakes', unexpected_flakes, |
+ max_tests=10) |
+ else: |
+ bb_annotations.PrintMsg('?? (results missing)') |
if options.factory_properties.get('archive_webkit_results', False): |
bb_annotations.PrintNamedStep('archive_webkit_results') |
base = 'https://storage.googleapis.com/chromium-layout-test-archives' |
builder_name = options.build_properties.get('buildername', '') |
build_number = str(options.build_properties.get('buildnumber', '')) |
- bb_annotations.PrintLink('results', |
- '%s/%s/%s/layout-test-results/results.html' % ( |
- base, EscapeBuilderName(builder_name), build_number)) |
+ results_link = '%s/%s/%s/layout-test-results/results.html' % ( |
+ base, EscapeBuilderName(builder_name), build_number) |
+ bb_annotations.PrintLink('results', results_link) |
bb_annotations.PrintLink('(zip)', '%s/%s/%s/layout-test-results.zip' % ( |
base, EscapeBuilderName(builder_name), build_number)) |
gs_bucket = 'gs://chromium-layout-test-archives' |
RunCmd([os.path.join(SLAVE_SCRIPTS_DIR, 'chromium', |
'archive_layout_test_results.py'), |
- '--results-dir', '../../layout-test-results', |
- '--build-dir', CHROME_OUT_DIR, |
- '--build-number', build_number, |
- '--builder-name', builder_name, |
- '--gs-bucket', gs_bucket]) |
+ '--results-dir', '../../layout-test-results', |
+ '--build-dir', CHROME_OUT_DIR, |
+ '--build-number', build_number, |
+ '--builder-name', builder_name, |
+ '--gs-bucket', gs_bucket]) |
+ |
+ |
+def _ParseLayoutTestResults(results): |
+ """Extract the failures from the test run.""" |
+ # Cloned from third_party/WebKit/Tools/Scripts/print-json-test-results |
+ tests = _ConvertTrieToFlatPaths(results['tests']) |
+ failures = {} |
+ flakes = {} |
+ passes = {} |
+ for (test, result) in tests.iteritems(): |
+ if result.get('is_unexpected'): |
+ actual_result = result['actual'] |
+ if ' PASS' in actual_result: |
+ flakes[test] = actual_result |
+ elif actual_result == 'PASS': |
+ passes[test] = result |
+ else: |
+ failures[test] = actual_result |
+ |
+ return (passes, failures, flakes) |
+ |
+ |
+def _ConvertTrieToFlatPaths(trie, prefix=None): |
+ """Flatten the trie of failures into a list.""" |
+ # Cloned from third_party/WebKit/Tools/Scripts/print-json-test-results |
+ result = {} |
+ for name, data in trie.iteritems(): |
+ if prefix: |
+ name = prefix + '/' + name |
+ |
+ if len(data) and 'actual' not in data and 'expected' not in data: |
+ result.update(_ConvertTrieToFlatPaths(data, name)) |
+ else: |
+ result[name] = data |
+ |
+ return result |
+ |
+ |
+def _PrintDashboardLink(link_text, tests, max_tests): |
+ """Add a link to the flakiness dashboard in the step annotations.""" |
+ if len(tests) > max_tests: |
+ test_list_text = ' '.join(tests[:max_tests]) + ' and more' |
+ else: |
+ test_list_text = ' '.join(tests) |
+ |
+ dashboard_base = ('http://test-results.appspot.com' |
+ '/dashboards/flakiness_dashboard.html#' |
+ 'master=ChromiumWebkit&tests=') |
+ |
+ bb_annotations.PrintLink('%d %s: %s' % |
+ (len(tests), link_text, test_list_text), |
+ dashboard_base + ','.join(tests)) |
def EscapeBuilderName(builder_name): |
@@ -292,6 +369,7 @@ def SpawnLogcatMonitor(): |
# Wait for logcat_monitor to pull existing logcat |
RunCmd(['sleep', '5']) |
+ |
def ProvisionDevices(options): |
bb_annotations.PrintNamedStep('provision_devices') |
@@ -317,8 +395,8 @@ def DeviceStatusCheck(_): |
def GetDeviceSetupStepCmds(): |
return [ |
- ('provision_devices', ProvisionDevices), |
- ('device_status_check', DeviceStatusCheck), |
+ ('provision_devices', ProvisionDevices), |
+ ('device_status_check', DeviceStatusCheck), |
] |
@@ -378,7 +456,7 @@ def UploadHTML(options, gs_base_dir, dir_to_upload, link_text, |
gs_path = '%s/%s/%s/%s' % (gs_base_dir, bot_id, revision, randhash) |
RunCmd([bb_utils.GSUTIL_PATH, 'cp', '-R', dir_to_upload, 'gs://%s' % gs_path]) |
bb_annotations.PrintLink(link_text, |
- '%s/%s/%s' % (gs_url, gs_path, link_rel_path)) |
+ '%s/%s/%s' % (gs_url, gs_path, link_rel_path)) |
def GenerateJavaCoverageReport(options): |
@@ -465,8 +543,8 @@ def GetDeviceStepsOptParser(): |
'only run if this is set.')) |
parser.add_option( |
'--flakiness-server', |
- help='The flakiness dashboard server to which the results should be ' |
- 'uploaded.') |
+ help=('The flakiness dashboard server to which the results should be ' |
+ 'uploaded.')) |
parser.add_option( |
'--auto-reconnect', action='store_true', |
help='Push script to device which restarts adbd on disconnections.') |