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

Side by Side Diff: build/android/pylib/local/device/local_device_instrumentation_test_run.py

Issue 2854823007: Move screenshot capture to Java-side. (Closed)
Patch Set: Move screenshot capture to Java-side. Created 3 years, 7 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
OLDNEW
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 time 9 import time
10 10
11 from devil.android import device_errors 11 from devil.android import device_errors
12 from devil.android import device_temp_file
12 from devil.android import flag_changer 13 from devil.android import flag_changer
13 from devil.android.sdk import shared_prefs 14 from devil.android.sdk import shared_prefs
14 from devil.utils import reraiser_thread 15 from devil.utils import reraiser_thread
15 from pylib import valgrind_tools 16 from pylib import valgrind_tools
16 from pylib.android import logdog_logcat_monitor 17 from pylib.android import logdog_logcat_monitor
17 from pylib.base import base_test_result 18 from pylib.base import base_test_result
18 from pylib.instrumentation import instrumentation_test_instance 19 from pylib.instrumentation import instrumentation_test_instance
19 from pylib.local.device import local_device_environment 20 from pylib.local.device import local_device_environment
20 from pylib.local.device import local_device_test_run 21 from pylib.local.device import local_device_test_run
21 from pylib.utils import google_storage_helper 22 from pylib.utils import google_storage_helper
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
125 def set_debug_app(): 126 def set_debug_app():
126 # Set debug app in order to enable reading command line flags on user 127 # Set debug app in order to enable reading command line flags on user
127 # builds 128 # builds
128 if self._test_instance.flags: 129 if self._test_instance.flags:
129 if not self._test_instance.package_info: 130 if not self._test_instance.package_info:
130 logging.error("Couldn't set debug app: no package info") 131 logging.error("Couldn't set debug app: no package info")
131 elif not self._test_instance.package_info.package: 132 elif not self._test_instance.package_info.package:
132 logging.error("Couldn't set debug app: no package defined") 133 logging.error("Couldn't set debug app: no package defined")
133 else: 134 else:
134 dev.RunShellCommand(['am', 'set-debug-app', '--persistent', 135 dev.RunShellCommand(['am', 'set-debug-app', '--persistent',
135 self._test_instance.package_info.package], 136 self._test_instance.package_info.package],
136 check_return=True) 137 check_return=True)
137 @trace_event.traced 138 @trace_event.traced
138 def edit_shared_prefs(): 139 def edit_shared_prefs():
139 for pref in self._test_instance.edit_shared_prefs: 140 for pref in self._test_instance.edit_shared_prefs:
140 prefs = shared_prefs.SharedPrefs(dev, pref['package'], 141 prefs = shared_prefs.SharedPrefs(dev, pref['package'],
141 pref['filename']) 142 pref['filename'])
142 prefs.Load() 143 prefs.Load()
143 for key in pref.get('remove', []): 144 for key in pref.get('remove', []):
144 try: 145 try:
145 prefs.Remove(key) 146 prefs.Remove(key)
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after
239 return tests 240 return tests
240 241
241 #override 242 #override
242 def _GetUniqueTestName(self, test): 243 def _GetUniqueTestName(self, test):
243 return instrumentation_test_instance.GetUniqueTestName(test) 244 return instrumentation_test_instance.GetUniqueTestName(test)
244 245
245 #override 246 #override
246 def _RunTest(self, device, test): 247 def _RunTest(self, device, test):
247 extras = {} 248 extras = {}
248 249
249 flags = None 250 flags_to_add = []
251 flags_to_remove = []
250 test_timeout_scale = None 252 test_timeout_scale = None
251 if self._test_instance.coverage_directory: 253 if self._test_instance.coverage_directory:
252 coverage_basename = '%s.ec' % ('%s_group' % test[0]['method'] 254 coverage_basename = '%s.ec' % ('%s_group' % test[0]['method']
253 if isinstance(test, list) else test['method']) 255 if isinstance(test, list) else test['method'])
254 extras['coverage'] = 'true' 256 extras['coverage'] = 'true'
255 coverage_directory = os.path.join( 257 coverage_directory = os.path.join(
256 device.GetExternalStoragePath(), 'chrome', 'test', 'coverage') 258 device.GetExternalStoragePath(), 'chrome', 'test', 'coverage')
257 coverage_device_file = os.path.join( 259 coverage_device_file = os.path.join(
258 coverage_directory, coverage_basename) 260 coverage_directory, coverage_basename)
259 extras['coverageFile'] = coverage_device_file 261 extras['coverageFile'] = coverage_device_file
262 # Save screenshot if screenshot dir is specified (save locally) or if
263 # a GS bucket is passed (save in cloud).
264 screenshot_device_file = None
jbudorick 2017/05/08 22:32:06 optional nit: could do something here similar to w
265 if (self._test_instance.screenshot_dir or
266 self._test_instance.gs_results_bucket):
267 screenshot_device_file = device_temp_file.DeviceTempFile(
268 device.adb, suffix='.png', dir=device.GetExternalStoragePath())
269 flags_to_add.append('--screenshot-file=%s' % screenshot_device_file.name)
260 270
261 if isinstance(test, list): 271 if isinstance(test, list):
262 if not self._test_instance.driver_apk: 272 if not self._test_instance.driver_apk:
263 raise Exception('driver_apk does not exist. ' 273 raise Exception('driver_apk does not exist. '
264 'Please build it and try again.') 274 'Please build it and try again.')
265 if any(t.get('is_junit4') for t in test): 275 if any(t.get('is_junit4') for t in test):
266 raise Exception('driver apk does not support JUnit4 tests') 276 raise Exception('driver apk does not support JUnit4 tests')
267 277
268 def name_and_timeout(t): 278 def name_and_timeout(t):
269 n = instrumentation_test_instance.GetTestName(t) 279 n = instrumentation_test_instance.GetTestName(t)
(...skipping 16 matching lines...) Expand all
286 test_display_name = self._GetUniqueTestName(test) 296 test_display_name = self._GetUniqueTestName(test)
287 if test['is_junit4']: 297 if test['is_junit4']:
288 target = '%s/%s' % ( 298 target = '%s/%s' % (
289 self._test_instance.test_package, 299 self._test_instance.test_package,
290 self._test_instance.test_runner_junit4) 300 self._test_instance.test_runner_junit4)
291 else: 301 else:
292 target = '%s/%s' % ( 302 target = '%s/%s' % (
293 self._test_instance.test_package, self._test_instance.test_runner) 303 self._test_instance.test_package, self._test_instance.test_runner)
294 extras['class'] = test_name 304 extras['class'] = test_name
295 if 'flags' in test: 305 if 'flags' in test:
296 flags = test['flags'] 306 flags_to_add.extend(test['flags'].add)
307 flags_to_remove.extend(test['flags'].remove)
297 timeout = self._GetTimeoutFromAnnotations( 308 timeout = self._GetTimeoutFromAnnotations(
298 test['annotations'], test_display_name) 309 test['annotations'], test_display_name)
299 310
300 test_timeout_scale = self._GetTimeoutScaleFromAnnotations( 311 test_timeout_scale = self._GetTimeoutScaleFromAnnotations(
301 test['annotations']) 312 test['annotations'])
302 if test_timeout_scale and test_timeout_scale != 1: 313 if test_timeout_scale and test_timeout_scale != 1:
303 valgrind_tools.SetChromeTimeoutScale( 314 valgrind_tools.SetChromeTimeoutScale(
304 device, test_timeout_scale * self._test_instance.timeout_scale) 315 device, test_timeout_scale * self._test_instance.timeout_scale)
305 316
306 logging.info('preparing to run %s: %s', test_display_name, test) 317 logging.info('preparing to run %s: %s', test_display_name, test)
307 318
308 if flags: 319 if flags_to_add or flags_to_remove:
309 self._CreateFlagChangerIfNeeded(device) 320 self._CreateFlagChangerIfNeeded(device)
310 self._flag_changers[str(device)].PushFlags( 321 self._flag_changers[str(device)].PushFlags(
311 add=flags.add, remove=flags.remove) 322 add=flags_to_add, remove=flags_to_remove)
312 323
313 try: 324 try:
314 device.RunShellCommand( 325 device.RunShellCommand(
315 ['log', '-p', 'i', '-t', _TAG, 'START %s' % test_name], 326 ['log', '-p', 'i', '-t', _TAG, 'START %s' % test_name],
316 check_return=True) 327 check_return=True)
317 time_ms = lambda: int(time.time() * 1e3) 328 time_ms = lambda: int(time.time() * 1e3)
318 start_ms = time_ms() 329 start_ms = time_ms()
319 330
320 stream_name = 'logcat_%s_%s_%s' % ( 331 stream_name = 'logcat_%s_%s_%s' % (
321 test_name.replace('#', '.'), 332 test_name.replace('#', '.'),
322 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()), 333 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()),
323 device.serial) 334 device.serial)
324 logmon = logdog_logcat_monitor.LogdogLogcatMonitor( 335 logmon = logdog_logcat_monitor.LogdogLogcatMonitor(
325 device.adb, stream_name, filter_specs=LOGCAT_FILTERS) 336 device.adb, stream_name, filter_specs=LOGCAT_FILTERS)
326 337
327 with contextlib_ext.Optional( 338 with contextlib_ext.Optional(
328 logmon, self._test_instance.should_save_logcat): 339 logmon, self._test_instance.should_save_logcat):
329 with contextlib_ext.Optional( 340 with contextlib_ext.Optional(
330 trace_event.trace(test_name), 341 trace_event.trace(test_name),
331 self._env.trace_output): 342 self._env.trace_output):
332 output = device.StartInstrumentation( 343 output = device.StartInstrumentation(
333 target, raw=True, extras=extras, timeout=timeout, retries=0) 344 target, raw=True, extras=extras, timeout=timeout, retries=0)
334 logcat_url = logmon.GetLogcatURL() 345 logcat_url = logmon.GetLogcatURL()
335 finally: 346 finally:
336 device.RunShellCommand( 347 device.RunShellCommand(
337 ['log', '-p', 'i', '-t', _TAG, 'END %s' % test_name], 348 ['log', '-p', 'i', '-t', _TAG, 'END %s' % test_name],
338 check_return=True) 349 check_return=True)
339 duration_ms = time_ms() - start_ms 350 duration_ms = time_ms() - start_ms
340 if flags: 351 if flags_to_add or flags_to_remove:
341 self._flag_changers[str(device)].Restore() 352 self._flag_changers[str(device)].Restore()
342 if test_timeout_scale: 353 if test_timeout_scale:
343 valgrind_tools.SetChromeTimeoutScale( 354 valgrind_tools.SetChromeTimeoutScale(
344 device, self._test_instance.timeout_scale) 355 device, self._test_instance.timeout_scale)
345 356
346 # TODO(jbudorick): Make instrumentation tests output a JSON so this 357 # TODO(jbudorick): Make instrumentation tests output a JSON so this
347 # doesn't have to parse the output. 358 # doesn't have to parse the output.
348 result_code, result_bundle, statuses = ( 359 result_code, result_bundle, statuses = (
349 self._test_instance.ParseAmInstrumentRawOutput(output)) 360 self._test_instance.ParseAmInstrumentRawOutput(output))
350 results = self._test_instance.GenerateTestResults( 361 results = self._test_instance.GenerateTestResults(
351 result_code, result_bundle, statuses, start_ms, duration_ms) 362 result_code, result_bundle, statuses, start_ms, duration_ms)
352 for result in results: 363 for result in results:
353 if logcat_url: 364 if logcat_url:
354 result.SetLink('logcat', logcat_url) 365 result.SetLink('logcat', logcat_url)
355 366
356 # Update the result name if the test used flags. 367 # Update the result name if the test used flags.
357 if flags: 368 if flags_to_add or flags_to_remove:
358 for r in results: 369 for r in results:
359 if r.GetName() == test_name: 370 if r.GetName() == test_name:
360 r.SetName(test_display_name) 371 r.SetName(test_display_name)
361 372
362 # Add UNKNOWN results for any missing tests. 373 # Add UNKNOWN results for any missing tests.
363 iterable_test = test if isinstance(test, list) else [test] 374 iterable_test = test if isinstance(test, list) else [test]
364 test_names = set(self._GetUniqueTestName(t) for t in iterable_test) 375 test_names = set(self._GetUniqueTestName(t) for t in iterable_test)
365 results_names = set(r.GetName() for r in results) 376 results_names = set(r.GetName() for r in results)
366 results.extend( 377 results.extend(
367 base_test_result.BaseTestResult(u, base_test_result.ResultType.UNKNOWN) 378 base_test_result.BaseTestResult(u, base_test_result.ResultType.UNKNOWN)
(...skipping 11 matching lines...) Expand all
379 # - clearing the application state while persisting permissions 390 # - clearing the application state while persisting permissions
380 if any(r.GetType() not in (base_test_result.ResultType.PASS, 391 if any(r.GetType() not in (base_test_result.ResultType.PASS,
381 base_test_result.ResultType.SKIP) 392 base_test_result.ResultType.SKIP)
382 for r in results): 393 for r in results):
383 with contextlib_ext.Optional( 394 with contextlib_ext.Optional(
384 tempfile_ext.NamedTemporaryDirectory(), 395 tempfile_ext.NamedTemporaryDirectory(),
385 self._test_instance.screenshot_dir is None and 396 self._test_instance.screenshot_dir is None and
386 self._test_instance.gs_results_bucket) as screenshot_host_dir: 397 self._test_instance.gs_results_bucket) as screenshot_host_dir:
387 screenshot_host_dir = ( 398 screenshot_host_dir = (
388 self._test_instance.screenshot_dir or screenshot_host_dir) 399 self._test_instance.screenshot_dir or screenshot_host_dir)
389 if screenshot_host_dir: 400 self._SaveScreenshot(device, screenshot_host_dir,
390 file_name = '%s-%s.png' % ( 401 screenshot_device_file, test_display_name,
391 test_display_name, 402 results)
392 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()))
393 screenshot_file = device.TakeScreenshot(
394 os.path.join(screenshot_host_dir, file_name))
395 logging.info(
396 'Saved screenshot for %s to %s.',
397 test_display_name, screenshot_file)
398 if self._test_instance.gs_results_bucket:
399 link = google_storage_helper.upload(
400 google_storage_helper.unique_name('screenshot', device=device),
401 screenshot_file,
402 bucket=self._test_instance.gs_results_bucket + '/screenshots')
403 for result in results:
404 result.SetLink('post_test_screenshot', link)
405 403
406 logging.info('detected failure in %s. raw output:', test_display_name) 404 logging.info('detected failure in %s. raw output:', test_display_name)
407 for l in output: 405 for l in output:
408 logging.info(' %s', l) 406 logging.info(' %s', l)
409 if (not self._env.skip_clear_data 407 if (not self._env.skip_clear_data
410 and self._test_instance.package_info): 408 and self._test_instance.package_info):
411 permissions = ( 409 permissions = (
412 self._test_instance.apk_under_test.GetPermissions() 410 self._test_instance.apk_under_test.GetPermissions()
413 if self._test_instance.apk_under_test 411 if self._test_instance.apk_under_test
414 else None) 412 else None)
(...skipping 20 matching lines...) Expand all
435 include_stack_symbols=False, 433 include_stack_symbols=False,
436 wipe_tombstones=True) 434 wipe_tombstones=True)
437 stream_name = 'tombstones_%s_%s' % ( 435 stream_name = 'tombstones_%s_%s' % (
438 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()), 436 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime()),
439 device.serial) 437 device.serial)
440 tombstones_url = logdog_helper.text( 438 tombstones_url = logdog_helper.text(
441 stream_name, '\n'.join(resolved_tombstones)) 439 stream_name, '\n'.join(resolved_tombstones))
442 result.SetLink('tombstones', tombstones_url) 440 result.SetLink('tombstones', tombstones_url)
443 return results, None 441 return results, None
444 442
443 def _SaveScreenshot(self, device, screenshot_host_dir, screenshot_device_file,
444 test_name, results):
445 if screenshot_host_dir:
446 screenshot_host_file = os.path.join(
447 screenshot_host_dir,
448 '%s-%s.png' % (
449 test_name,
450 time.strftime('%Y%m%dT%H%M%S-UTC', time.gmtime())))
451 if device.FileExists(screenshot_device_file.name):
452 try:
453 device.PullFile(screenshot_device_file.name, screenshot_host_file)
454 finally:
455 screenshot_device_file.close()
456
457 logging.info(
458 'Saved screenshot for %s to %s.',
459 test_name, screenshot_host_file)
460 if self._test_instance.gs_results_bucket:
461 link = google_storage_helper.upload(
462 google_storage_helper.unique_name(
463 'screenshot', device=device),
464 screenshot_host_file,
465 bucket=('%s/screenshots' %
466 self._test_instance.gs_results_bucket))
467 for result in results:
468 result.SetLink('post_test_screenshot', link)
469
445 #override 470 #override
446 def _ShouldRetry(self, test): 471 def _ShouldRetry(self, test):
447 if 'RetryOnFailure' in test.get('annotations', {}): 472 if 'RetryOnFailure' in test.get('annotations', {}):
448 return True 473 return True
449 474
450 # TODO(jbudorick): Remove this log message once @RetryOnFailure has been 475 # TODO(jbudorick): Remove this log message once @RetryOnFailure has been
451 # enabled for a while. See crbug.com/619055 for more details. 476 # enabled for a while. See crbug.com/619055 for more details.
452 logging.error('Default retries are being phased out. crbug.com/619055') 477 logging.error('Default retries are being phased out. crbug.com/619055')
453 return False 478 return False
454 479
(...skipping 15 matching lines...) Expand all
470 if k in annotations: 495 if k in annotations:
471 timeout = v 496 timeout = v
472 break 497 break
473 else: 498 else:
474 logging.warning('Using default 1 minute timeout for %s', test_name) 499 logging.warning('Using default 1 minute timeout for %s', test_name)
475 timeout = 60 500 timeout = 60
476 501
477 timeout *= cls._GetTimeoutScaleFromAnnotations(annotations) 502 timeout *= cls._GetTimeoutScaleFromAnnotations(annotations)
478 503
479 return timeout 504 return timeout
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698