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 | |
30 _DEFAULT_ANNOTATIONS = [ | 26 _DEFAULT_ANNOTATIONS = [ |
31 'Smoke', 'SmallTest', 'MediumTest', 'LargeTest', | 27 'Smoke', 'SmallTest', 'MediumTest', 'LargeTest', |
32 'EnormousTest', 'IntegrationTest'] | 28 'EnormousTest', 'IntegrationTest'] |
33 _NATIVE_CRASH_RE = re.compile('native crash', re.IGNORECASE) | 29 _NATIVE_CRASH_RE = re.compile('native crash', re.IGNORECASE) |
34 _PICKLE_FORMAT_VERSION = 10 | 30 _PICKLE_FORMAT_VERSION = 10 |
35 | 31 |
36 | 32 |
37 # TODO(jbudorick): Make these private class methods of | 33 # TODO(jbudorick): Make these private class methods of |
38 # InstrumentationTestInstance once the instrumentation test_runner is | 34 # InstrumentationTestInstance once the instrumentation test_runner is |
39 # deprecated. | 35 # deprecated. |
(...skipping 11 matching lines...) Expand all Loading... |
51 - the status code as an integer | 47 - the status code as an integer |
52 - the bundle dump as a dict mapping string keys to a list of | 48 - the bundle dump as a dict mapping string keys to a list of |
53 strings, one for each line. | 49 strings, one for each line. |
54 """ | 50 """ |
55 parser = instrumentation_parser.InstrumentationParser(raw_output) | 51 parser = instrumentation_parser.InstrumentationParser(raw_output) |
56 statuses = list(parser.IterStatus()) | 52 statuses = list(parser.IterStatus()) |
57 code, bundle = parser.GetResult() | 53 code, bundle = parser.GetResult() |
58 return (code, bundle, statuses) | 54 return (code, bundle, statuses) |
59 | 55 |
60 | 56 |
61 def GenerateTestResults( | 57 def GenerateTestResult(test_name, instr_statuses, start_ms, duration_ms): |
62 result_code, result_bundle, statuses, start_ms, duration_ms): | 58 """Generate the result of |test| from |instr_statuses|. |
63 """Generate test results from |statuses|. | |
64 | 59 |
65 Args: | 60 Args: |
66 result_code: The overall status code as an integer. | 61 test_name: The name of the test as "class#method" |
67 result_bundle: The summary bundle dump as a dict. | 62 instr_statuses: A list of 2-tuples containing: |
68 statuses: A list of 2-tuples containing: | |
69 - the status code as an integer | 63 - the status code as an integer |
70 - the bundle dump as a dict mapping string keys to string values | 64 - the bundle dump as a dict mapping string keys to string values |
71 Note that this is the same as the third item in the 3-tuple returned by | 65 Note that this is the same as the third item in the 3-tuple returned by |
72 |_ParseAmInstrumentRawOutput|. | 66 |_ParseAmInstrumentRawOutput|. |
73 start_ms: The start time of the test in milliseconds. | 67 start_ms: The start time of the test in milliseconds. |
74 duration_ms: The duration of the test in milliseconds. | 68 duration_ms: The duration of the test in milliseconds. |
| 69 Returns: |
| 70 An InstrumentationTestResult object. |
| 71 """ |
| 72 log = '' |
| 73 result_type = base_test_result.ResultType.UNKNOWN |
75 | 74 |
76 Returns: | 75 for status_code, bundle in instr_statuses: |
77 A list containing an instance of InstrumentationTestResult for each test | 76 if status_code == instrumentation_parser.STATUS_CODE_START: |
78 parsed. | 77 pass |
79 """ | 78 elif status_code == instrumentation_parser.STATUS_CODE_OK: |
| 79 bundle_test = '%s#%s' % (bundle.get('class', ''), bundle.get('test', '')) |
| 80 skipped = bundle.get('test_skipped', '') |
80 | 81 |
81 results = [] | 82 if (test_name == bundle_test and |
| 83 result_type == base_test_result.ResultType.UNKNOWN): |
| 84 result_type = base_test_result.ResultType.PASS |
| 85 elif skipped.lower() in ('true', '1', 'yes'): |
| 86 result_type = base_test_result.ResultType.SKIP |
| 87 logging.info('Skipped ' + test_name) |
| 88 else: |
| 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'] |
82 | 96 |
83 current_result = None | 97 return test_result.InstrumentationTestResult( |
84 | 98 test_name, result_type, start_ms, duration_ms, log=log) |
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 | |
93 if status_code == instrumentation_parser.STATUS_CODE_START: | |
94 if current_result: | |
95 results.append(current_result) | |
96 current_result = test_result.InstrumentationTestResult( | |
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']) | |
112 | |
113 if current_result: | |
114 if current_result.GetType() == base_test_result.ResultType.UNKNOWN: | |
115 crashed = (result_code == _ACTIVITY_RESULT_CANCELED | |
116 and any(_NATIVE_CRASH_RE.search(l) | |
117 for l in result_bundle.itervalues())) | |
118 if crashed: | |
119 current_result.SetType(base_test_result.ResultType.CRASH) | |
120 | |
121 results.append(current_result) | |
122 | |
123 return results | |
124 | 99 |
125 | 100 |
126 class InstrumentationTestInstance(test_instance.TestInstance): | 101 class InstrumentationTestInstance(test_instance.TestInstance): |
127 | 102 |
128 def __init__(self, args, isolate_delegate, error_func): | 103 def __init__(self, args, isolate_delegate, error_func): |
129 super(InstrumentationTestInstance, self).__init__() | 104 super(InstrumentationTestInstance, self).__init__() |
130 | 105 |
131 self._apk_under_test = None | 106 self._apk_under_test = None |
132 self._package_info = None | 107 self._package_info = None |
133 self._test_apk = None | 108 self._test_apk = None |
(...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
439 a = dict(c['annotations']) | 414 a = dict(c['annotations']) |
440 a.update(m['annotations']) | 415 a.update(m['annotations']) |
441 inflated_tests.append({ | 416 inflated_tests.append({ |
442 'class': c['class'], | 417 'class': c['class'], |
443 'method': m['method'], | 418 'method': m['method'], |
444 'annotations': a, | 419 'annotations': a, |
445 }) | 420 }) |
446 return inflated_tests | 421 return inflated_tests |
447 | 422 |
448 @staticmethod | 423 @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 GenerateTestResults( | 453 def GenerateTestResult(test_name, instr_statuses, start_ms, duration_ms): |
454 result_code, result_bundle, statuses, start_ms, duration_ms): | 454 return GenerateTestResult(test_name, instr_statuses, start_ms, duration_ms) |
455 return GenerateTestResults(result_code, result_bundle, statuses, | |
456 start_ms, duration_ms) | |
457 | 455 |
458 #override | 456 #override |
459 def TearDown(self): | 457 def TearDown(self): |
460 if self._isolate_delegate: | 458 if self._isolate_delegate: |
461 self._isolate_delegate.Clear() | 459 self._isolate_delegate.Clear() |
462 | 460 |
OLD | NEW |