| OLD | NEW |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 # Copyright (c) 2012 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 """Class for running instrumentation tests on a single device.""" | 5 """Class for running instrumentation tests on a single device.""" |
| 6 | 6 |
| 7 import logging | 7 import logging |
| 8 import os | 8 import os |
| 9 import re | 9 import re |
| 10 import sys | 10 import sys |
| 11 import time | 11 import time |
| 12 | 12 |
| 13 from pylib import constants | 13 from pylib import constants |
| 14 from pylib import flag_changer | 14 from pylib import flag_changer |
| 15 from pylib import valgrind_tools | 15 from pylib import valgrind_tools |
| 16 from pylib.base import base_test_result | 16 from pylib.base import base_test_result |
| 17 from pylib.base import base_test_runner | 17 from pylib.base import base_test_runner |
| 18 from pylib.device import device_errors | 18 from pylib.device import device_errors |
| 19 from pylib.instrumentation import instrumentation_test_instance |
| 19 from pylib.instrumentation import json_perf_parser | 20 from pylib.instrumentation import json_perf_parser |
| 20 from pylib.instrumentation import test_result | 21 from pylib.instrumentation import test_result |
| 22 from pylib.local.device import local_device_instrumentation_test_run |
| 21 | 23 |
| 22 sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'util', 'lib', | 24 sys.path.append(os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'util', 'lib', |
| 23 'common')) | 25 'common')) |
| 24 import perf_tests_results_helper # pylint: disable=F0401 | 26 import perf_tests_results_helper # pylint: disable=F0401 |
| 25 | 27 |
| 26 | 28 |
| 27 _PERF_TEST_ANNOTATION = 'PerfTest' | 29 _PERF_TEST_ANNOTATION = 'PerfTest' |
| 28 | 30 |
| 29 | 31 |
| 30 class TestRunner(base_test_runner.BaseTestRunner): | 32 class TestRunner(base_test_runner.BaseTestRunner): |
| (...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 312 | 314 |
| 313 Returns: | 315 Returns: |
| 314 The raw output of am instrument as a list of lines. | 316 The raw output of am instrument as a list of lines. |
| 315 """ | 317 """ |
| 316 extras = self._GetInstrumentationArgs() | 318 extras = self._GetInstrumentationArgs() |
| 317 extras['class'] = test | 319 extras['class'] = test |
| 318 return self.device.StartInstrumentation( | 320 return self.device.StartInstrumentation( |
| 319 '%s/%s' % (self.test_pkg.GetPackageName(), self.options.test_runner), | 321 '%s/%s' % (self.test_pkg.GetPackageName(), self.options.test_runner), |
| 320 raw=True, extras=extras, timeout=timeout, retries=0) | 322 raw=True, extras=extras, timeout=timeout, retries=0) |
| 321 | 323 |
| 322 @staticmethod | |
| 323 def _ParseAmInstrumentRawOutput(raw_output): | |
| 324 """Parses the output of an |am instrument -r| call. | |
| 325 | |
| 326 Args: | |
| 327 raw_output: the output of an |am instrument -r| call as a list of lines | |
| 328 Returns: | |
| 329 A 3-tuple containing: | |
| 330 - the instrumentation code as an integer | |
| 331 - the instrumentation result as a list of lines | |
| 332 - the instrumentation statuses received as a list of 2-tuples | |
| 333 containing: | |
| 334 - the status code as an integer | |
| 335 - the bundle dump as a dict mapping string keys to a list of | |
| 336 strings, one for each line. | |
| 337 """ | |
| 338 INSTR_STATUS = 'INSTRUMENTATION_STATUS: ' | |
| 339 INSTR_STATUS_CODE = 'INSTRUMENTATION_STATUS_CODE: ' | |
| 340 INSTR_RESULT = 'INSTRUMENTATION_RESULT: ' | |
| 341 INSTR_CODE = 'INSTRUMENTATION_CODE: ' | |
| 342 | |
| 343 last = None | |
| 344 instr_code = None | |
| 345 instr_result = [] | |
| 346 instr_statuses = [] | |
| 347 bundle = {} | |
| 348 for line in raw_output: | |
| 349 if line.startswith(INSTR_STATUS): | |
| 350 instr_var = line[len(INSTR_STATUS):] | |
| 351 if '=' in instr_var: | |
| 352 k, v = instr_var.split('=', 1) | |
| 353 bundle[k] = [v] | |
| 354 last = INSTR_STATUS | |
| 355 last_key = k | |
| 356 else: | |
| 357 logging.debug('Unknown "%s" line: %s' % (INSTR_STATUS, line)) | |
| 358 | |
| 359 elif line.startswith(INSTR_STATUS_CODE): | |
| 360 instr_status = line[len(INSTR_STATUS_CODE):] | |
| 361 instr_statuses.append((int(instr_status), bundle)) | |
| 362 bundle = {} | |
| 363 last = INSTR_STATUS_CODE | |
| 364 | |
| 365 elif line.startswith(INSTR_RESULT): | |
| 366 instr_result.append(line[len(INSTR_RESULT):]) | |
| 367 last = INSTR_RESULT | |
| 368 | |
| 369 elif line.startswith(INSTR_CODE): | |
| 370 instr_code = int(line[len(INSTR_CODE):]) | |
| 371 last = INSTR_CODE | |
| 372 | |
| 373 elif last == INSTR_STATUS: | |
| 374 bundle[last_key].append(line) | |
| 375 | |
| 376 elif last == INSTR_RESULT: | |
| 377 instr_result.append(line) | |
| 378 | |
| 379 return (instr_code, instr_result, instr_statuses) | |
| 380 | |
| 381 def _GenerateTestResult(self, test, instr_statuses, start_ms, duration_ms): | |
| 382 """Generate the result of |test| from |instr_statuses|. | |
| 383 | |
| 384 Args: | |
| 385 instr_statuses: A list of 2-tuples containing: | |
| 386 - the status code as an integer | |
| 387 - the bundle dump as a dict mapping string keys to string values | |
| 388 Note that this is the same as the third item in the 3-tuple returned by | |
| 389 |_ParseAmInstrumentRawOutput|. | |
| 390 start_ms: The start time of the test in milliseconds. | |
| 391 duration_ms: The duration of the test in milliseconds. | |
| 392 Returns: | |
| 393 An InstrumentationTestResult object. | |
| 394 """ | |
| 395 INSTR_STATUS_CODE_START = 1 | |
| 396 INSTR_STATUS_CODE_OK = 0 | |
| 397 INSTR_STATUS_CODE_ERROR = -1 | |
| 398 INSTR_STATUS_CODE_FAIL = -2 | |
| 399 | |
| 400 log = '' | |
| 401 result_type = base_test_result.ResultType.UNKNOWN | |
| 402 | |
| 403 for status_code, bundle in instr_statuses: | |
| 404 if status_code == INSTR_STATUS_CODE_START: | |
| 405 pass | |
| 406 elif status_code == INSTR_STATUS_CODE_OK: | |
| 407 bundle_test = '%s#%s' % ( | |
| 408 ''.join(bundle.get('class', [''])), | |
| 409 ''.join(bundle.get('test', ['']))) | |
| 410 skipped = ''.join(bundle.get('test_skipped', [''])) | |
| 411 | |
| 412 if (test == bundle_test and | |
| 413 result_type == base_test_result.ResultType.UNKNOWN): | |
| 414 result_type = base_test_result.ResultType.PASS | |
| 415 elif skipped.lower() in ('true', '1', 'yes'): | |
| 416 result_type = base_test_result.ResultType.SKIP | |
| 417 logging.info('Skipped ' + test) | |
| 418 else: | |
| 419 if status_code not in (INSTR_STATUS_CODE_ERROR, | |
| 420 INSTR_STATUS_CODE_FAIL): | |
| 421 logging.info('Unrecognized status code %d. Handling as an error.', | |
| 422 status_code) | |
| 423 result_type = base_test_result.ResultType.FAIL | |
| 424 if 'stack' in bundle: | |
| 425 log = '\n'.join(bundle['stack']) | |
| 426 # Dismiss any error dialogs. Limit the number in case we have an error | |
| 427 # loop or we are failing to dismiss. | |
| 428 for _ in xrange(10): | |
| 429 package = self.device.old_interface.DismissCrashDialogIfNeeded() | |
| 430 if not package: | |
| 431 break | |
| 432 # Assume test package convention of ".test" suffix | |
| 433 if package in self.test_pkg.GetPackageName(): | |
| 434 result_type = base_test_result.ResultType.CRASH | |
| 435 break | |
| 436 | |
| 437 return test_result.InstrumentationTestResult( | |
| 438 test, result_type, start_ms, duration_ms, log=log) | |
| 439 | |
| 440 #override | 324 #override |
| 441 def RunTest(self, test): | 325 def RunTest(self, test): |
| 442 results = base_test_result.TestRunResults() | 326 results = base_test_result.TestRunResults() |
| 443 timeout = (self._GetIndividualTestTimeoutSecs(test) * | 327 timeout = (self._GetIndividualTestTimeoutSecs(test) * |
| 444 self._GetIndividualTestTimeoutScale(test) * | 328 self._GetIndividualTestTimeoutScale(test) * |
| 445 self.tool.GetTimeoutScale()) | 329 self.tool.GetTimeoutScale()) |
| 446 if (self.device.build_version_sdk | 330 if (self.device.build_version_sdk |
| 447 < constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN): | 331 < constants.ANDROID_SDK_VERSION_CODES.JELLY_BEAN): |
| 448 timeout *= 10 | 332 timeout *= 10 |
| 449 | 333 |
| 450 start_ms = 0 | 334 start_ms = 0 |
| 451 duration_ms = 0 | 335 duration_ms = 0 |
| 452 try: | 336 try: |
| 453 self.TestSetup(test) | 337 self.TestSetup(test) |
| 454 | 338 |
| 455 time_ms = lambda: int(time.time() * 1000) | 339 time_ms = lambda: int(time.time() * 1000) |
| 456 start_ms = time_ms() | 340 start_ms = time_ms() |
| 457 raw_output = self._RunTest(test, timeout) | 341 raw_output = self._RunTest(test, timeout) |
| 458 duration_ms = time_ms() - start_ms | 342 duration_ms = time_ms() - start_ms |
| 459 | 343 |
| 460 # Parse the test output | 344 # Parse the test output |
| 461 _, _, statuses = self._ParseAmInstrumentRawOutput(raw_output) | 345 _, _, statuses = ( |
| 462 result = self._GenerateTestResult(test, statuses, start_ms, duration_ms) | 346 instrumentation_test_instance.ParseAmInstrumentRawOutput(raw_output)) |
| 347 result = instrumentation_test_instance.GenerateTestResult( |
| 348 test, statuses, start_ms, duration_ms) |
| 349 if local_device_instrumentation_test_run.DidPackageCrashOnDevice( |
| 350 self.test_pkg.GetPackageName(), self.device): |
| 351 result.SetType(base_test_result.ResultType.CRASH) |
| 463 results.AddResult(result) | 352 results.AddResult(result) |
| 464 except device_errors.CommandTimeoutError as e: | 353 except device_errors.CommandTimeoutError as e: |
| 465 results.AddResult(test_result.InstrumentationTestResult( | 354 results.AddResult(test_result.InstrumentationTestResult( |
| 466 test, base_test_result.ResultType.TIMEOUT, start_ms, duration_ms, | 355 test, base_test_result.ResultType.TIMEOUT, start_ms, duration_ms, |
| 467 log=str(e) or 'No information')) | 356 log=str(e) or 'No information')) |
| 468 except device_errors.DeviceUnreachableError as e: | 357 except device_errors.DeviceUnreachableError as e: |
| 469 results.AddResult(test_result.InstrumentationTestResult( | 358 results.AddResult(test_result.InstrumentationTestResult( |
| 470 test, base_test_result.ResultType.CRASH, start_ms, duration_ms, | 359 test, base_test_result.ResultType.CRASH, start_ms, duration_ms, |
| 471 log=str(e) or 'No information')) | 360 log=str(e) or 'No information')) |
| 472 self.TestTeardown(test, results) | 361 self.TestTeardown(test, results) |
| 473 return (results, None if results.DidRunPass() else test) | 362 return (results, None if results.DidRunPass() else test) |
| OLD | NEW |