Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(446)

Side by Side Diff: build/android/buildbot/bb_device_steps.py

Issue 26330004: Print actual list of failing tests on Android layout test steps (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: more linting Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 hashlib 8 import hashlib
9 import json
9 import multiprocessing 10 import multiprocessing
10 import os 11 import os
11 import random 12 import random
12 import re 13 import re
13 import shutil 14 import shutil
14 import sys 15 import sys
15 16
16 import bb_utils 17 import bb_utils
17 import bb_annotations 18 import bb_annotations
18 19
(...skipping 20 matching lines...) Expand all
39 # apk_package: package for the apk to be installed. 40 # apk_package: package for the apk to be installed.
40 # test_apk: apk to run tests on. 41 # test_apk: apk to run tests on.
41 # test_data: data folder in format destination:source. 42 # test_data: data folder in format destination:source.
42 # host_driven_root: The host-driven test root directory. 43 # host_driven_root: The host-driven test root directory.
43 # annotation: Annotation of the tests to include. 44 # annotation: Annotation of the tests to include.
44 # exclude_annotation: The annotation of the tests to exclude. 45 # exclude_annotation: The annotation of the tests to exclude.
45 I_TEST = collections.namedtuple('InstrumentationTest', [ 46 I_TEST = collections.namedtuple('InstrumentationTest', [
46 'name', 'apk', 'apk_package', 'test_apk', 'test_data', 'host_driven_root', 47 'name', 'apk', 'apk_package', 'test_apk', 'test_data', 'host_driven_root',
47 'annotation', 'exclude_annotation', 'extra_flags']) 48 'annotation', 'exclude_annotation', 'extra_flags'])
48 49
50
49 def I(name, apk, apk_package, test_apk, test_data, host_driven_root=None, 51 def I(name, apk, apk_package, test_apk, test_data, host_driven_root=None,
50 annotation=None, exclude_annotation=None, extra_flags=None): 52 annotation=None, exclude_annotation=None, extra_flags=None):
51 return I_TEST(name, apk, apk_package, test_apk, test_data, host_driven_root, 53 return I_TEST(name, apk, apk_package, test_apk, test_data, host_driven_root,
52 annotation, exclude_annotation, extra_flags) 54 annotation, exclude_annotation, extra_flags)
53 55
54 INSTRUMENTATION_TESTS = dict((suite.name, suite) for suite in [ 56 INSTRUMENTATION_TESTS = dict((suite.name, suite) for suite in [
55 I('ContentShell', 57 I('ContentShell',
56 'ContentShell.apk', 58 'ContentShell.apk',
57 'org.chromium.content_shell_apk', 59 'org.chromium.content_shell_apk',
58 'ContentShellTest', 60 'ContentShellTest',
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
134 args.append('--release') 136 args.append('--release')
135 if options.asan: 137 if options.asan:
136 args.append('--tool=asan') 138 args.append('--tool=asan')
137 for suite in suites: 139 for suite in suites:
138 bb_annotations.PrintNamedStep(suite) 140 bb_annotations.PrintNamedStep(suite)
139 cmd = ['build/android/test_runner.py', 'gtest', '-s', suite] + args 141 cmd = ['build/android/test_runner.py', 'gtest', '-s', suite] + args
140 if suite == 'content_browsertests': 142 if suite == 'content_browsertests':
141 cmd.append('--num_retries=1') 143 cmd.append('--num_retries=1')
142 RunCmd(cmd) 144 RunCmd(cmd)
143 145
146
144 def RunChromeDriverTests(options): 147 def RunChromeDriverTests(options):
145 """Run all the steps for running chromedriver tests.""" 148 """Run all the steps for running chromedriver tests."""
146 bb_annotations.PrintNamedStep('chromedriver_annotation') 149 bb_annotations.PrintNamedStep('chromedriver_annotation')
147 RunCmd(['chrome/test/chromedriver/run_buildbot_steps.py', 150 RunCmd(['chrome/test/chromedriver/run_buildbot_steps.py',
148 '--android-packages=%s,%s,%s' % 151 '--android-packages=%s,%s,%s' %
149 (constants.PACKAGE_INFO['chromium_test_shell'].package, 152 (constants.PACKAGE_INFO['chromium_test_shell'].package,
150 constants.PACKAGE_INFO['chrome_stable'].package, 153 constants.PACKAGE_INFO['chrome_stable'].package,
151 constants.PACKAGE_INFO['chrome_beta'].package), 154 constants.PACKAGE_INFO['chrome_beta'].package),
152 '--revision=%s' % _GetRevision(options), 155 '--revision=%s' % _GetRevision(options),
153 '--update-log']) 156 '--update-log'])
154 157
158
155 def InstallApk(options, test, print_step=False): 159 def InstallApk(options, test, print_step=False):
156 """Install an apk to all phones. 160 """Install an apk to all phones.
157 161
158 Args: 162 Args:
159 options: options object 163 options: options object
160 test: An I_TEST namedtuple 164 test: An I_TEST namedtuple
161 print_step: Print a buildbot step 165 print_step: Print a buildbot step
162 """ 166 """
163 if print_step: 167 if print_step:
164 bb_annotations.PrintNamedStep('install_%s' % test.name.lower()) 168 bb_annotations.PrintNamedStep('install_%s' % test.name.lower())
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
220 RunCmd(['webkit/tools/layout_tests/run_webkit_tests.py', 224 RunCmd(['webkit/tools/layout_tests/run_webkit_tests.py',
221 '--lint-test-files', 225 '--lint-test-files',
222 '--chromium', 226 '--chromium',
223 '--target', target]) 227 '--target', target])
224 228
225 229
226 def RunWebkitLayoutTests(options): 230 def RunWebkitLayoutTests(options):
227 """Run layout tests on an actual device.""" 231 """Run layout tests on an actual device."""
228 bb_annotations.PrintNamedStep('webkit_tests') 232 bb_annotations.PrintNamedStep('webkit_tests')
229 cmd_args = [ 233 cmd_args = [
230 '--no-show-results', 234 '--no-show-results',
231 '--no-new-test-results', 235 '--no-new-test-results',
232 '--full-results-html', 236 '--full-results-html',
233 '--clobber-old-results', 237 '--clobber-old-results',
234 '--exit-after-n-failures', '5000', 238 '--exit-after-n-failures', '5000',
235 '--exit-after-n-crashes-or-timeouts', '100', 239 '--exit-after-n-crashes-or-timeouts', '100',
236 '--debug-rwt-logging', 240 '--debug-rwt-logging',
237 '--results-directory', '../layout-test-results', 241 '--results-directory', '../layout-test-results',
238 '--target', options.target, 242 '--target', options.target,
239 '--builder-name', options.build_properties.get('buildername', ''), 243 '--builder-name', options.build_properties.get('buildername', ''),
240 '--build-number', str(options.build_properties.get('buildnumber', '')), 244 '--build-number', str(options.build_properties.get('buildnumber', '')),
241 '--master-name', 'ChromiumWebkit', # TODO: Get this from the cfg. 245 '--master-name', 'ChromiumWebkit', # TODO: Get this from the cfg.
242 '--build-name', options.build_properties.get('buildername', ''), 246 '--build-name', options.build_properties.get('buildername', ''),
243 '--platform=android'] 247 '--platform=android']
244 248
245 for flag in 'test_results_server', 'driver_name', 'additional_drt_flag': 249 for flag in 'test_results_server', 'driver_name', 'additional_drt_flag':
246 if flag in options.factory_properties: 250 if flag in options.factory_properties:
247 cmd_args.extend(['--%s' % flag.replace('_', '-'), 251 cmd_args.extend(['--%s' % flag.replace('_', '-'),
248 options.factory_properties.get(flag)]) 252 options.factory_properties.get(flag)])
249 253
250 for f in options.factory_properties.get('additional_expectations', []): 254 for f in options.factory_properties.get('additional_expectations', []):
251 cmd_args.extend( 255 cmd_args.extend(
252 ['--additional-expectations=%s' % os.path.join(CHROME_SRC_DIR, *f)]) 256 ['--additional-expectations=%s' % os.path.join(CHROME_SRC_DIR, *f)])
253 257
254 # TODO(dpranke): Remove this block after 258 # TODO(dpranke): Remove this block after
255 # https://codereview.chromium.org/12927002/ lands. 259 # https://codereview.chromium.org/12927002/ lands.
256 for f in options.factory_properties.get('additional_expectations_files', []): 260 for f in options.factory_properties.get('additional_expectations_files', []):
257 cmd_args.extend( 261 cmd_args.extend(
258 ['--additional-expectations=%s' % os.path.join(CHROME_SRC_DIR, *f)]) 262 ['--additional-expectations=%s' % os.path.join(CHROME_SRC_DIR, *f)])
259 263
260 RunCmd(['webkit/tools/layout_tests/run_webkit_tests.py'] + cmd_args) 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)')
261 286
262 if options.factory_properties.get('archive_webkit_results', False): 287 if options.factory_properties.get('archive_webkit_results', False):
263 bb_annotations.PrintNamedStep('archive_webkit_results') 288 bb_annotations.PrintNamedStep('archive_webkit_results')
264 base = 'https://storage.googleapis.com/chromium-layout-test-archives' 289 base = 'https://storage.googleapis.com/chromium-layout-test-archives'
265 builder_name = options.build_properties.get('buildername', '') 290 builder_name = options.build_properties.get('buildername', '')
266 build_number = str(options.build_properties.get('buildnumber', '')) 291 build_number = str(options.build_properties.get('buildnumber', ''))
267 bb_annotations.PrintLink('results', 292 results_link = '%s/%s/%s/layout-test-results/results.html' % (
268 '%s/%s/%s/layout-test-results/results.html' % ( 293 base, EscapeBuilderName(builder_name), build_number)
269 base, EscapeBuilderName(builder_name), build_number)) 294 bb_annotations.PrintLink('results', results_link)
270 bb_annotations.PrintLink('(zip)', '%s/%s/%s/layout-test-results.zip' % ( 295 bb_annotations.PrintLink('(zip)', '%s/%s/%s/layout-test-results.zip' % (
271 base, EscapeBuilderName(builder_name), build_number)) 296 base, EscapeBuilderName(builder_name), build_number))
272 gs_bucket = 'gs://chromium-layout-test-archives' 297 gs_bucket = 'gs://chromium-layout-test-archives'
273 RunCmd([os.path.join(SLAVE_SCRIPTS_DIR, 'chromium', 298 RunCmd([os.path.join(SLAVE_SCRIPTS_DIR, 'chromium',
274 'archive_layout_test_results.py'), 299 'archive_layout_test_results.py'),
275 '--results-dir', '../../layout-test-results', 300 '--results-dir', '../../layout-test-results',
276 '--build-dir', CHROME_OUT_DIR, 301 '--build-dir', CHROME_OUT_DIR,
277 '--build-number', build_number, 302 '--build-number', build_number,
278 '--builder-name', builder_name, 303 '--builder-name', builder_name,
279 '--gs-bucket', gs_bucket]) 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))
280 357
281 358
282 def EscapeBuilderName(builder_name): 359 def EscapeBuilderName(builder_name):
283 return re.sub('[ ()]', '_', builder_name) 360 return re.sub('[ ()]', '_', builder_name)
284 361
285 362
286 def SpawnLogcatMonitor(): 363 def SpawnLogcatMonitor():
287 shutil.rmtree(LOGCAT_DIR, ignore_errors=True) 364 shutil.rmtree(LOGCAT_DIR, ignore_errors=True)
288 bb_utils.SpawnCmd([ 365 bb_utils.SpawnCmd([
289 os.path.join(CHROME_SRC_DIR, 'build', 'android', 'adb_logcat_monitor.py'), 366 os.path.join(CHROME_SRC_DIR, 'build', 'android', 'adb_logcat_monitor.py'),
290 LOGCAT_DIR]) 367 LOGCAT_DIR])
291 368
292 # Wait for logcat_monitor to pull existing logcat 369 # Wait for logcat_monitor to pull existing logcat
293 RunCmd(['sleep', '5']) 370 RunCmd(['sleep', '5'])
294 371
372
295 def ProvisionDevices(options): 373 def ProvisionDevices(options):
296 bb_annotations.PrintNamedStep('provision_devices') 374 bb_annotations.PrintNamedStep('provision_devices')
297 375
298 if not bb_utils.TESTING: 376 if not bb_utils.TESTING:
299 # Restart adb to work around bugs, sleep to wait for usb discovery. 377 # Restart adb to work around bugs, sleep to wait for usb discovery.
300 adb = android_commands.AndroidCommands() 378 adb = android_commands.AndroidCommands()
301 adb.RestartAdbServer() 379 adb.RestartAdbServer()
302 RunCmd(['sleep', '1']) 380 RunCmd(['sleep', '1'])
303 381
304 if options.reboot: 382 if options.reboot:
305 RebootDevices() 383 RebootDevices()
306 provision_cmd = ['build/android/provision_devices.py', '-t', options.target] 384 provision_cmd = ['build/android/provision_devices.py', '-t', options.target]
307 if options.auto_reconnect: 385 if options.auto_reconnect:
308 provision_cmd.append('--auto-reconnect') 386 provision_cmd.append('--auto-reconnect')
309 RunCmd(provision_cmd) 387 RunCmd(provision_cmd)
310 388
311 389
312 def DeviceStatusCheck(_): 390 def DeviceStatusCheck(_):
313 bb_annotations.PrintNamedStep('device_status_check') 391 bb_annotations.PrintNamedStep('device_status_check')
314 RunCmd(['build/android/buildbot/bb_device_status_check.py'], 392 RunCmd(['build/android/buildbot/bb_device_status_check.py'],
315 halt_on_failure=True) 393 halt_on_failure=True)
316 394
317 395
318 def GetDeviceSetupStepCmds(): 396 def GetDeviceSetupStepCmds():
319 return [ 397 return [
320 ('provision_devices', ProvisionDevices), 398 ('provision_devices', ProvisionDevices),
321 ('device_status_check', DeviceStatusCheck), 399 ('device_status_check', DeviceStatusCheck),
322 ] 400 ]
323 401
324 402
325 def RunUnitTests(options): 403 def RunUnitTests(options):
326 RunTestSuites(options, gtest_config.STABLE_TEST_SUITES) 404 RunTestSuites(options, gtest_config.STABLE_TEST_SUITES)
327 405
328 406
329 def RunInstrumentationTests(options): 407 def RunInstrumentationTests(options):
330 for test in INSTRUMENTATION_TESTS.itervalues(): 408 for test in INSTRUMENTATION_TESTS.itervalues():
331 RunInstrumentationSuite(options, test) 409 RunInstrumentationSuite(options, test)
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
371 link_text: Link text to be displayed on the step. 449 link_text: Link text to be displayed on the step.
372 link_rel_path: Link path relative to |dir_to_upload|. 450 link_rel_path: Link path relative to |dir_to_upload|.
373 gs_url: Google storage URL. 451 gs_url: Google storage URL.
374 """ 452 """
375 revision = _GetRevision(options) 453 revision = _GetRevision(options)
376 bot_id = options.build_properties.get('buildername', 'testing') 454 bot_id = options.build_properties.get('buildername', 'testing')
377 randhash = hashlib.sha1(str(random.random())).hexdigest() 455 randhash = hashlib.sha1(str(random.random())).hexdigest()
378 gs_path = '%s/%s/%s/%s' % (gs_base_dir, bot_id, revision, randhash) 456 gs_path = '%s/%s/%s/%s' % (gs_base_dir, bot_id, revision, randhash)
379 RunCmd([bb_utils.GSUTIL_PATH, 'cp', '-R', dir_to_upload, 'gs://%s' % gs_path]) 457 RunCmd([bb_utils.GSUTIL_PATH, 'cp', '-R', dir_to_upload, 'gs://%s' % gs_path])
380 bb_annotations.PrintLink(link_text, 458 bb_annotations.PrintLink(link_text,
381 '%s/%s/%s' % (gs_url, gs_path, link_rel_path)) 459 '%s/%s/%s' % (gs_url, gs_path, link_rel_path))
382 460
383 461
384 def GenerateJavaCoverageReport(options): 462 def GenerateJavaCoverageReport(options):
385 """Generates an HTML coverage report using EMMA and uploads it.""" 463 """Generates an HTML coverage report using EMMA and uploads it."""
386 bb_annotations.PrintNamedStep('java_coverage_report') 464 bb_annotations.PrintNamedStep('java_coverage_report')
387 465
388 coverage_html = os.path.join(options.coverage_dir, 'coverage_html') 466 coverage_html = os.path.join(options.coverage_dir, 'coverage_html')
389 RunCmd(['build/android/generate_emma_html.py', 467 RunCmd(['build/android/generate_emma_html.py',
390 '--coverage-dir', options.coverage_dir, 468 '--coverage-dir', options.coverage_dir,
391 '--metadata-dir', os.path.join(CHROME_OUT_DIR, options.target), 469 '--metadata-dir', os.path.join(CHROME_OUT_DIR, options.target),
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
458 parser.add_option('--asan', action='store_true', help='Run tests with asan.') 536 parser.add_option('--asan', action='store_true', help='Run tests with asan.')
459 parser.add_option('--install', metavar='<apk name>', 537 parser.add_option('--install', metavar='<apk name>',
460 help='Install an apk by name') 538 help='Install an apk by name')
461 parser.add_option('--reboot', action='store_true', 539 parser.add_option('--reboot', action='store_true',
462 help='Reboot devices before running tests') 540 help='Reboot devices before running tests')
463 parser.add_option('--coverage-bucket', 541 parser.add_option('--coverage-bucket',
464 help=('Bucket name to store coverage results. Coverage is ' 542 help=('Bucket name to store coverage results. Coverage is '
465 'only run if this is set.')) 543 'only run if this is set.'))
466 parser.add_option( 544 parser.add_option(
467 '--flakiness-server', 545 '--flakiness-server',
468 help='The flakiness dashboard server to which the results should be ' 546 help=('The flakiness dashboard server to which the results should be '
469 'uploaded.') 547 'uploaded.'))
470 parser.add_option( 548 parser.add_option(
471 '--auto-reconnect', action='store_true', 549 '--auto-reconnect', action='store_true',
472 help='Push script to device which restarts adbd on disconnections.') 550 help='Push script to device which restarts adbd on disconnections.')
473 parser.add_option( 551 parser.add_option(
474 '--logcat-dump-output', 552 '--logcat-dump-output',
475 help='The logcat dump output will be "tee"-ed into this file') 553 help='The logcat dump output will be "tee"-ed into this file')
476 554
477 return parser 555 return parser
478 556
479 557
(...skipping 11 matching lines...) Expand all
491 setattr(options, 'target', options.factory_properties.get('target', 'Debug')) 569 setattr(options, 'target', options.factory_properties.get('target', 'Debug'))
492 if options.coverage_bucket: 570 if options.coverage_bucket:
493 setattr(options, 'coverage_dir', 571 setattr(options, 'coverage_dir',
494 os.path.join(CHROME_OUT_DIR, options.target, 'coverage')) 572 os.path.join(CHROME_OUT_DIR, options.target, 'coverage'))
495 573
496 MainTestWrapper(options) 574 MainTestWrapper(options)
497 575
498 576
499 if __name__ == '__main__': 577 if __name__ == '__main__':
500 sys.exit(main(sys.argv)) 578 sys.exit(main(sys.argv))
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698