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 |