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 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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |