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 pickle | 7 import pickle |
8 import re | 8 import re |
9 import sys | 9 import sys |
10 | 10 |
11 from pylib import cmd_helper | 11 from pylib import cmd_helper |
12 from pylib import constants | 12 from pylib import constants |
13 from pylib import flag_changer | 13 from pylib import flag_changer |
14 from pylib.base import base_test_result | 14 from pylib.base import base_test_result |
15 from pylib.base import test_instance | 15 from pylib.base import test_instance |
16 from pylib.instrumentation import test_result | 16 from pylib.instrumentation import test_result |
17 from pylib.instrumentation import instrumentation_parser | 17 from pylib.instrumentation import instrumentation_parser |
18 from pylib.utils import apk_helper | 18 from pylib.utils import apk_helper |
19 from pylib.utils import md5sum | 19 from pylib.utils import md5sum |
20 from pylib.utils import proguard | 20 from pylib.utils import proguard |
21 | 21 |
22 sys.path.append( | 22 sys.path.append( |
23 os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'util', 'lib', 'common')) | 23 os.path.join(constants.DIR_SOURCE_ROOT, 'build', 'util', 'lib', 'common')) |
24 import unittest_util | 24 import unittest_util |
25 | 25 |
| 26 # Ref: http://developer.android.com/reference/android/app/Activity.html |
| 27 _ACTIVITY_RESULT_CANCELED = 0 |
| 28 _ACTIVITY_RESULT_OK = -1 |
| 29 |
26 _DEFAULT_ANNOTATIONS = [ | 30 _DEFAULT_ANNOTATIONS = [ |
27 'Smoke', 'SmallTest', 'MediumTest', 'LargeTest', | 31 'Smoke', 'SmallTest', 'MediumTest', 'LargeTest', |
28 'EnormousTest', 'IntegrationTest'] | 32 'EnormousTest', 'IntegrationTest'] |
29 _NATIVE_CRASH_RE = re.compile('native crash', re.IGNORECASE) | 33 _NATIVE_CRASH_RE = re.compile('native crash', re.IGNORECASE) |
30 _PICKLE_FORMAT_VERSION = 10 | 34 _PICKLE_FORMAT_VERSION = 10 |
31 | 35 |
32 | 36 |
33 # TODO(jbudorick): Make these private class methods of | 37 # TODO(jbudorick): Make these private class methods of |
34 # InstrumentationTestInstance once the instrumentation test_runner is | 38 # InstrumentationTestInstance once the instrumentation test_runner is |
35 # deprecated. | 39 # deprecated. |
(...skipping 11 matching lines...) Expand all Loading... |
47 - the status code as an integer | 51 - the status code as an integer |
48 - the bundle dump as a dict mapping string keys to a list of | 52 - the bundle dump as a dict mapping string keys to a list of |
49 strings, one for each line. | 53 strings, one for each line. |
50 """ | 54 """ |
51 parser = instrumentation_parser.InstrumentationParser(raw_output) | 55 parser = instrumentation_parser.InstrumentationParser(raw_output) |
52 statuses = list(parser.IterStatus()) | 56 statuses = list(parser.IterStatus()) |
53 code, bundle = parser.GetResult() | 57 code, bundle = parser.GetResult() |
54 return (code, bundle, statuses) | 58 return (code, bundle, statuses) |
55 | 59 |
56 | 60 |
57 def GenerateTestResult(test_name, instr_statuses, start_ms, duration_ms): | 61 def GenerateTestResults( |
58 """Generate the result of |test| from |instr_statuses|. | 62 result_code, result_bundle, statuses, start_ms, duration_ms): |
| 63 """Generate test results from |statuses|. |
59 | 64 |
60 Args: | 65 Args: |
61 test_name: The name of the test as "class#method" | 66 result_code: The overall status code as an integer. |
62 instr_statuses: A list of 2-tuples containing: | 67 result_bundle: The summary bundle dump as a dict. |
| 68 statuses: A list of 2-tuples containing: |
63 - the status code as an integer | 69 - the status code as an integer |
64 - the bundle dump as a dict mapping string keys to string values | 70 - the bundle dump as a dict mapping string keys to string values |
65 Note that this is the same as the third item in the 3-tuple returned by | 71 Note that this is the same as the third item in the 3-tuple returned by |
66 |_ParseAmInstrumentRawOutput|. | 72 |_ParseAmInstrumentRawOutput|. |
67 start_ms: The start time of the test in milliseconds. | 73 start_ms: The start time of the test in milliseconds. |
68 duration_ms: The duration of the test in milliseconds. | 74 duration_ms: The duration of the test in milliseconds. |
| 75 |
69 Returns: | 76 Returns: |
70 An InstrumentationTestResult object. | 77 A list containing an instance of InstrumentationTestResult for each test |
| 78 parsed. |
71 """ | 79 """ |
72 log = '' | |
73 result_type = base_test_result.ResultType.UNKNOWN | |
74 | 80 |
75 for status_code, bundle in instr_statuses: | 81 results = [] |
| 82 |
| 83 current_result = None |
| 84 |
| 85 for status_code, bundle in statuses: |
| 86 test_class = bundle.get('class', '') |
| 87 test_method = bundle.get('test', '') |
| 88 if test_class and test_method: |
| 89 test_name = '%s#%s' % (test_class, test_method) |
| 90 else: |
| 91 continue |
| 92 |
76 if status_code == instrumentation_parser.STATUS_CODE_START: | 93 if status_code == instrumentation_parser.STATUS_CODE_START: |
77 pass | 94 if current_result: |
78 elif status_code == instrumentation_parser.STATUS_CODE_OK: | 95 results.append(current_result) |
79 bundle_test = '%s#%s' % (bundle.get('class', ''), bundle.get('test', '')) | 96 current_result = test_result.InstrumentationTestResult( |
80 skipped = bundle.get('test_skipped', '') | 97 test_name, base_test_result.ResultType.UNKNOWN, start_ms, duration_ms) |
| 98 else: |
| 99 if status_code == instrumentation_parser.STATUS_CODE_OK: |
| 100 if bundle.get('test_skipped', '').lower() in ('true', '1', 'yes'): |
| 101 current_result.SetType(base_test_result.ResultType.SKIP) |
| 102 elif current_result.GetType() == base_test_result.ResultType.UNKNOWN: |
| 103 current_result.SetType(base_test_result.ResultType.PASS) |
| 104 else: |
| 105 if status_code not in (instrumentation_parser.STATUS_CODE_ERROR, |
| 106 instrumentation_parser.STATUS_CODE_FAILURE): |
| 107 logging.error('Unrecognized status code %d. Handling as an error.', |
| 108 status_code) |
| 109 current_result.SetType(base_test_result.ResultType.FAIL) |
| 110 if 'stack' in bundle: |
| 111 current_result.SetLog(bundle['stack']) |
81 | 112 |
82 if (test_name == bundle_test and | 113 if current_result: |
83 result_type == base_test_result.ResultType.UNKNOWN): | 114 if current_result.GetType() == base_test_result.ResultType.UNKNOWN: |
84 result_type = base_test_result.ResultType.PASS | 115 crashed = (result_code == _ACTIVITY_RESULT_CANCELED |
85 elif skipped.lower() in ('true', '1', 'yes'): | 116 and any(_NATIVE_CRASH_RE.search(l) |
86 result_type = base_test_result.ResultType.SKIP | 117 for l in result_bundle.itervalues())) |
87 logging.info('Skipped ' + test_name) | 118 if crashed: |
88 else: | 119 current_result.SetType(base_test_result.ResultType.CRASH) |
89 if status_code not in (instrumentation_parser.STATUS_CODE_ERROR, | |
90 instrumentation_parser.STATUS_CODE_FAILURE): | |
91 logging.error('Unrecognized status code %d. Handling as an error.', | |
92 status_code) | |
93 result_type = base_test_result.ResultType.FAIL | |
94 if 'stack' in bundle: | |
95 log = bundle['stack'] | |
96 | 120 |
97 return test_result.InstrumentationTestResult( | 121 results.append(current_result) |
98 test_name, result_type, start_ms, duration_ms, log=log) | 122 |
| 123 return results |
99 | 124 |
100 | 125 |
101 class InstrumentationTestInstance(test_instance.TestInstance): | 126 class InstrumentationTestInstance(test_instance.TestInstance): |
102 | 127 |
103 def __init__(self, args, isolate_delegate, error_func): | 128 def __init__(self, args, isolate_delegate, error_func): |
104 super(InstrumentationTestInstance, self).__init__() | 129 super(InstrumentationTestInstance, self).__init__() |
105 | 130 |
106 self._apk_under_test = None | 131 self._apk_under_test = None |
107 self._package_info = None | 132 self._package_info = None |
108 self._test_apk = None | 133 self._test_apk = None |
(...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
414 a = dict(c['annotations']) | 439 a = dict(c['annotations']) |
415 a.update(m['annotations']) | 440 a.update(m['annotations']) |
416 inflated_tests.append({ | 441 inflated_tests.append({ |
417 'class': c['class'], | 442 'class': c['class'], |
418 'method': m['method'], | 443 'method': m['method'], |
419 'annotations': a, | 444 'annotations': a, |
420 }) | 445 }) |
421 return inflated_tests | 446 return inflated_tests |
422 | 447 |
423 @staticmethod | 448 @staticmethod |
424 def GenerateMultiTestResult(errors, statuses): | |
425 results = [] | |
426 skip_counter = 1 | |
427 for status_code, bundle in statuses: | |
428 if status_code != instrumentation_parser.STATUS_CODE_START: | |
429 # TODO(rnephew): Make skipped tests still output test name. This is only | |
430 # there to give skipped tests a unique name so they are counted | |
431 if 'test_skipped' in bundle: | |
432 test_name = str(skip_counter) | |
433 skip_counter += 1 | |
434 else: | |
435 test_name = '%s#%s' % (bundle.get('class', ''), | |
436 bundle.get('test', '')) | |
437 | |
438 results.append( | |
439 GenerateTestResult(test_name, [(status_code, bundle)], 0, 0)) | |
440 for error in errors.itervalues(): | |
441 if _NATIVE_CRASH_RE.search(error): | |
442 results.append( | |
443 base_test_result.BaseTestResult( | |
444 'Crash detected', base_test_result.ResultType.CRASH)) | |
445 | |
446 return results | |
447 | |
448 @staticmethod | |
449 def ParseAmInstrumentRawOutput(raw_output): | 449 def ParseAmInstrumentRawOutput(raw_output): |
450 return ParseAmInstrumentRawOutput(raw_output) | 450 return ParseAmInstrumentRawOutput(raw_output) |
451 | 451 |
452 @staticmethod | 452 @staticmethod |
453 def GenerateTestResult(test_name, instr_statuses, start_ms, duration_ms): | 453 def GenerateTestResults( |
454 return GenerateTestResult(test_name, instr_statuses, start_ms, duration_ms) | 454 result_code, result_bundle, statuses, start_ms, duration_ms): |
| 455 return GenerateTestResults(result_code, result_bundle, statuses, |
| 456 start_ms, duration_ms) |
455 | 457 |
456 #override | 458 #override |
457 def TearDown(self): | 459 def TearDown(self): |
458 if self._isolate_delegate: | 460 if self._isolate_delegate: |
459 self._isolate_delegate.Clear() | 461 self._isolate_delegate.Clear() |
460 | 462 |
OLD | NEW |