Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 # Copyright 2015 The Chromium Authors. All rights reserved. | 1 # Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import logging | 5 import logging |
| 6 import os | 6 import os |
| 7 import posixpath | 7 import posixpath |
| 8 import re | 8 import re |
| 9 import shutil | |
| 10 import tempfile | |
| 9 import time | 11 import time |
| 10 | 12 |
| 11 from devil.android import device_errors | 13 from devil.android import device_errors |
| 12 from devil.android import flag_changer | 14 from devil.android import flag_changer |
| 13 from devil.utils import reraiser_thread | 15 from devil.utils import reraiser_thread |
| 14 from pylib import valgrind_tools | 16 from pylib import valgrind_tools |
| 15 from pylib.android import logdog_logcat_monitor | 17 from pylib.android import logdog_logcat_monitor |
| 18 from pylib.constants import host_paths | |
| 16 from pylib.base import base_test_result | 19 from pylib.base import base_test_result |
| 17 from pylib.instrumentation import instrumentation_test_instance | 20 from pylib.instrumentation import instrumentation_test_instance |
| 18 from pylib.local.device import local_device_environment | 21 from pylib.local.device import local_device_environment |
| 19 from pylib.local.device import local_device_test_run | 22 from pylib.local.device import local_device_test_run |
| 20 from pylib.utils import logdog_helper | 23 from pylib.utils import logdog_helper |
| 21 from py_trace_event import trace_event | 24 from py_trace_event import trace_event |
| 22 from py_utils import contextlib_ext | 25 from py_utils import contextlib_ext |
| 23 import tombstones | 26 import tombstones |
| 24 | 27 |
| 25 _TAG = 'test_runner_py' | 28 _TAG = 'test_runner_py' |
| 26 | 29 |
| 27 TIMEOUT_ANNOTATIONS = [ | 30 TIMEOUT_ANNOTATIONS = [ |
| 28 ('Manual', 10 * 60 * 60), | 31 ('Manual', 10 * 60 * 60), |
| 29 ('IntegrationTest', 30 * 60), | 32 ('IntegrationTest', 30 * 60), |
| 30 ('External', 10 * 60), | 33 ('External', 10 * 60), |
| 31 ('EnormousTest', 10 * 60), | 34 ('EnormousTest', 10 * 60), |
| 32 ('LargeTest', 5 * 60), | 35 ('LargeTest', 5 * 60), |
| 33 ('MediumTest', 3 * 60), | 36 ('MediumTest', 3 * 60), |
| 34 ('SmallTest', 1 * 60), | 37 ('SmallTest', 1 * 60), |
| 35 ] | 38 ] |
| 36 | 39 |
| 40 _RE_RENDER_IMAGE_NAME = re.compile( | |
| 41 r'(?P<test_class>\w+)\.' | |
| 42 r'(?P<description>\w+)\.' | |
| 43 r'(?P<device_model>\w+)\.' | |
| 44 r'(?P<orientation>port|land)\.png') | |
| 45 | |
| 46 RENDER_TESTS_RESULTS_DIR = { | |
| 47 'chrome_public_test_apk': 'chrome/test/data/android/render_tests' | |
| 48 } | |
| 37 | 49 |
| 38 # TODO(jbudorick): Make this private once the instrumentation test_runner is | 50 # TODO(jbudorick): Make this private once the instrumentation test_runner is |
| 39 # deprecated. | 51 # deprecated. |
| 40 def DidPackageCrashOnDevice(package_name, device): | 52 def DidPackageCrashOnDevice(package_name, device): |
| 41 # Dismiss any error dialogs. Limit the number in case we have an error | 53 # Dismiss any error dialogs. Limit the number in case we have an error |
| 42 # loop or we are failing to dismiss. | 54 # loop or we are failing to dismiss. |
| 43 try: | 55 try: |
| 44 for _ in xrange(10): | 56 for _ in xrange(10): |
| 45 package = device.DismissCrashDialogIfNeeded() | 57 package = device.DismissCrashDialogIfNeeded() |
| 46 if not package: | 58 if not package: |
| (...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 253 self._flag_changers[str(device)].PushFlags( | 265 self._flag_changers[str(device)].PushFlags( |
| 254 add=flags.add, remove=flags.remove) | 266 add=flags.add, remove=flags.remove) |
| 255 | 267 |
| 256 try: | 268 try: |
| 257 device.RunShellCommand( | 269 device.RunShellCommand( |
| 258 ['log', '-p', 'i', '-t', _TAG, 'START %s' % test_name], | 270 ['log', '-p', 'i', '-t', _TAG, 'START %s' % test_name], |
| 259 check_return=True) | 271 check_return=True) |
| 260 time_ms = lambda: int(time.time() * 1e3) | 272 time_ms = lambda: int(time.time() * 1e3) |
| 261 start_ms = time_ms() | 273 start_ms = time_ms() |
| 262 | 274 |
| 263 stream_name = 'logcat_%s_%s_%s' % ( | 275 stream_name = 'logcat_%s_%s_%s' % ( |
|
jbudorick
2017/02/21 18:32:39
Can you use unique_name here?
| |
| 264 test_name.replace('#', '.'), | 276 test_name.replace('#', '.'), |
| 265 time.strftime('%Y%m%dT%H%M%S', time.localtime()), | 277 time.strftime('%Y%m%dT%H%M%S', time.localtime()), |
| 266 device.serial) | 278 device.serial) |
| 267 logmon = logdog_logcat_monitor.LogdogLogcatMonitor( | 279 logmon = logdog_logcat_monitor.LogdogLogcatMonitor( |
| 268 device.adb, stream_name) | 280 device.adb, stream_name) |
| 269 with contextlib_ext.Optional( | 281 with contextlib_ext.Optional( |
| 270 logmon, self._test_instance.should_save_logcat): | 282 logmon, self._test_instance.should_save_logcat): |
| 271 with contextlib_ext.Optional( | 283 with contextlib_ext.Optional( |
| 272 trace_event.trace(test_name), | 284 trace_event.trace(test_name), |
| 273 self._env.trace_output): | 285 self._env.trace_output): |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 288 # TODO(jbudorick): Make instrumentation tests output a JSON so this | 300 # TODO(jbudorick): Make instrumentation tests output a JSON so this |
| 289 # doesn't have to parse the output. | 301 # doesn't have to parse the output. |
| 290 result_code, result_bundle, statuses = ( | 302 result_code, result_bundle, statuses = ( |
| 291 self._test_instance.ParseAmInstrumentRawOutput(output)) | 303 self._test_instance.ParseAmInstrumentRawOutput(output)) |
| 292 results = self._test_instance.GenerateTestResults( | 304 results = self._test_instance.GenerateTestResults( |
| 293 result_code, result_bundle, statuses, start_ms, duration_ms) | 305 result_code, result_bundle, statuses, start_ms, duration_ms) |
| 294 for result in results: | 306 for result in results: |
| 295 if logcat_url: | 307 if logcat_url: |
| 296 result.SetLink('logcat', logcat_url) | 308 result.SetLink('logcat', logcat_url) |
| 297 | 309 |
| 310 # Save render test results. | |
| 311 if self._test_instance.should_save_images: | |
|
jbudorick
2017/02/21 18:37:12
Also, would it make sense to extract this into its
mikecase (-- gone --)
2017/02/23 00:21:26
Done
| |
| 312 if self._test_instance.suite in RENDER_TESTS_RESULTS_DIR: | |
| 313 render_results_dir = RENDER_TESTS_RESULTS_DIR[self._test_instance.suite] | |
| 314 | |
| 315 temp_dir = None | |
|
jbudorick
2017/02/21 18:32:40
https://chromium.googlesource.com/external/github.
mikecase (-- gone --)
2017/02/23 00:21:26
Done
| |
| 316 try: | |
| 317 temp_dir = tempfile.mkdtemp() | |
| 318 | |
| 319 failure_images_device_dir = posixpath.join( | |
| 320 device.GetExternalStoragePath(), | |
| 321 'chromium_tests_root', render_results_dir, 'failures') | |
| 322 device.PullFile(failure_images_device_dir, temp_dir) | |
| 323 device.RemovePath(failure_images_device_dir) | |
| 324 | |
| 325 for failure_filename in os.listdir( | |
|
jbudorick
2017/02/21 18:37:12
Of note, though: process_render_test_results attem
mikecase (-- gone --)
2017/02/23 00:21:26
Added diffing back. Makes this a bit more complex.
| |
| 326 os.path.join(temp_dir, 'failures')): | |
| 327 | |
| 328 m = _RE_RENDER_IMAGE_NAME.match(failure_filename) | |
|
jbudorick
2017/02/21 18:32:39
Do you do anything with m other than check it here
mikecase (-- gone --)
2017/02/23 00:21:26
no
| |
| 329 if not m: | |
| 330 logging.warning('Unexpected file in render test failures, %s', | |
|
jbudorick
2017/02/21 18:32:39
nit: "... failures: %s"
^
mikecase (-- gone --)
2017/02/23 00:21:26
Done
| |
| 331 failure_filename) | |
| 332 continue | |
| 333 | |
| 334 failure_filepath = os.path.join( | |
| 335 temp_dir, 'failures', failure_filename) | |
| 336 | |
| 337 link = logdog_helper.image( | |
| 338 logdog_helper.unique_name(failure_filename, device=device), | |
| 339 failure_filepath) | |
| 340 for result in results: | |
| 341 result.SetLink('%s_FAIL' % failure_filename, link) | |
|
jbudorick
2017/02/21 18:32:39
nit: name the link something else. Not immediately
mikecase (-- gone --)
2017/02/23 00:21:26
I changed this since I didnt want three separate l
| |
| 342 | |
| 343 golden_filepath = os.path.join( | |
| 344 host_paths.DIR_SOURCE_ROOT, render_results_dir, | |
| 345 failure_filename) | |
| 346 if not os.path.exists(golden_filepath): | |
| 347 logging.error('Cannot find golden image for %s', failure_filename) | |
| 348 continue | |
| 349 | |
| 350 link = logdog_helper.image( | |
| 351 logdog_helper.unique_name(failure_filename, device=device), | |
| 352 golden_filepath) | |
| 353 for result in results: | |
| 354 result.SetLink('%s_GOLDEN' % failure_filename, link) | |
|
jbudorick
2017/02/21 18:32:39
nit: same
mikecase (-- gone --)
2017/02/23 00:21:26
See above.
| |
| 355 | |
| 356 finally: | |
| 357 if temp_dir: | |
| 358 shutil.rmtree(temp_dir) | |
| 359 | |
| 298 # Update the result name if the test used flags. | 360 # Update the result name if the test used flags. |
| 299 if flags: | 361 if flags: |
| 300 for r in results: | 362 for r in results: |
| 301 if r.GetName() == test_name: | 363 if r.GetName() == test_name: |
| 302 r.SetName(test_display_name) | 364 r.SetName(test_display_name) |
| 303 | 365 |
| 304 # Add UNKNOWN results for any missing tests. | 366 # Add UNKNOWN results for any missing tests. |
| 305 iterable_test = test if isinstance(test, list) else [test] | 367 iterable_test = test if isinstance(test, list) else [test] |
| 306 test_names = set(self._GetUniqueTestName(t) for t in iterable_test) | 368 test_names = set(self._GetUniqueTestName(t) for t in iterable_test) |
| 307 results_names = set(r.GetName() for r in results) | 369 results_names = set(r.GetName() for r in results) |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 319 # - optionally taking a screenshot | 381 # - optionally taking a screenshot |
| 320 # - logging the raw output at INFO level | 382 # - logging the raw output at INFO level |
| 321 # - clearing the application state while persisting permissions | 383 # - clearing the application state while persisting permissions |
| 322 if any(r.GetType() not in (base_test_result.ResultType.PASS, | 384 if any(r.GetType() not in (base_test_result.ResultType.PASS, |
| 323 base_test_result.ResultType.SKIP) | 385 base_test_result.ResultType.SKIP) |
| 324 for r in results): | 386 for r in results): |
| 325 if self._test_instance.screenshot_dir: | 387 if self._test_instance.screenshot_dir: |
| 326 file_name = '%s-%s.png' % ( | 388 file_name = '%s-%s.png' % ( |
| 327 test_display_name, | 389 test_display_name, |
| 328 time.strftime('%Y%m%dT%H%M%S', time.localtime())) | 390 time.strftime('%Y%m%dT%H%M%S', time.localtime())) |
| 329 saved_dir = device.TakeScreenshot( | 391 screenshot_file = device.TakeScreenshot( |
| 330 os.path.join(self._test_instance.screenshot_dir, file_name)) | 392 os.path.join(self._test_instance.screenshot_dir, file_name)) |
| 331 logging.info( | 393 logging.info( |
| 332 'Saved screenshot for %s to %s.', | 394 'Saved screenshot for %s to %s.', |
| 333 test_display_name, saved_dir) | 395 test_display_name, screenshot_file) |
| 396 if self._test_instance.should_save_images: | |
| 397 link = logdog_helper.image( | |
| 398 logdog_helper.unique_name('screenshot', device=device), | |
| 399 screenshot_file) | |
| 400 for result in results: | |
| 401 result.SetLink('failure_screenshot', link) | |
| 402 | |
| 334 logging.info('detected failure in %s. raw output:', test_display_name) | 403 logging.info('detected failure in %s. raw output:', test_display_name) |
| 335 for l in output: | 404 for l in output: |
| 336 logging.info(' %s', l) | 405 logging.info(' %s', l) |
| 337 if (not self._env.skip_clear_data | 406 if (not self._env.skip_clear_data |
| 338 and self._test_instance.package_info): | 407 and self._test_instance.package_info): |
| 339 permissions = ( | 408 permissions = ( |
| 340 self._test_instance.apk_under_test.GetPermissions() | 409 self._test_instance.apk_under_test.GetPermissions() |
| 341 if self._test_instance.apk_under_test | 410 if self._test_instance.apk_under_test |
| 342 else None) | 411 else None) |
| 343 device.ClearApplicationState(self._test_instance.package_info.package, | 412 device.ClearApplicationState(self._test_instance.package_info.package, |
| 344 permissions=permissions) | 413 permissions=permissions) |
| 345 | |
| 346 else: | 414 else: |
| 347 logging.debug('raw output from %s:', test_display_name) | 415 logging.debug('raw output from %s:', test_display_name) |
| 348 for l in output: | 416 for l in output: |
| 349 logging.debug(' %s', l) | 417 logging.debug(' %s', l) |
| 350 if self._test_instance.coverage_directory: | 418 if self._test_instance.coverage_directory: |
| 351 device.PullFile(coverage_directory, | 419 device.PullFile(coverage_directory, |
| 352 self._test_instance.coverage_directory) | 420 self._test_instance.coverage_directory) |
| 353 device.RunShellCommand('rm -f %s' % os.path.join(coverage_directory, | 421 device.RunShellCommand('rm -f %s' % os.path.join(coverage_directory, |
| 354 '*')) | 422 '*')) |
| 355 if self._test_instance.store_tombstones: | 423 if self._test_instance.store_tombstones: |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 399 timeout = v | 467 timeout = v |
| 400 break | 468 break |
| 401 else: | 469 else: |
| 402 logging.warning('Using default 1 minute timeout for %s', test_name) | 470 logging.warning('Using default 1 minute timeout for %s', test_name) |
| 403 timeout = 60 | 471 timeout = 60 |
| 404 | 472 |
| 405 timeout *= cls._GetTimeoutScaleFromAnnotations(annotations) | 473 timeout *= cls._GetTimeoutScaleFromAnnotations(annotations) |
| 406 | 474 |
| 407 return timeout | 475 return timeout |
| 408 | 476 |
| OLD | NEW |