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 |