OLD | NEW |
---|---|
(Empty) | |
1 #!/usr/bin/env python | |
2 # Copyright (c) 2016 The Chromium Authors. All rights reserved. | |
3 # Use of this source code is governed by a BSD-style license that can be | |
4 # found in the LICENSE file. | |
5 | |
6 """Finds all the tracked(disabled/flaky) tests from proguard dump""" | |
7 | |
8 import argparse | |
9 import datetime | |
10 import json | |
11 import linecache | |
12 import logging | |
13 import os | |
14 import pprint | |
15 import re | |
16 import sys | |
17 import time | |
18 | |
19 _SRC_DIR = os.path.abspath(os.path.join( | |
20 os.path.dirname(__file__), '..', '..')) | |
21 | |
22 sys.path.append(os.path.join(_SRC_DIR, 'build', 'android')) | |
23 | |
24 from pylib import constants | |
25 from pylib.instrumentation import instrumentation_test_instance | |
26 | |
27 sys.path.append(os.path.join(_SRC_DIR, 'third_party', 'catapult', 'devil')) | |
28 from devil.utils import cmd_helper | |
29 | |
30 _GIT_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S' | |
31 _EXPORT_TIME_FORMAT = '%Y%m%dT%H%M%S' | |
32 _EXCLUDE_ANNOTATION_LIST = ["CommandLineFlags$Add"] | |
perezju
2016/04/25 12:45:08
why excluded?
Yoland Yan(Google)
2016/04/26 01:10:08
Because I thought annotation also has string eleme
| |
33 | |
34 class _JSONKeyName(object): | |
perezju
2016/04/25 12:45:08
I would still vote for removing this constant defi
Yoland Yan(Google)
2016/04/26 01:10:08
Got it
| |
35 TEST_MASTER_KEY = 'tests' | |
36 REPORT_MASTER_KEY = 'metadata' | |
37 REVISION_KEY = 'revision' | |
38 COMMIT_POS_KEY = 'commit_pos' | |
39 ANNOTATIONS_KEY = 'annotations' | |
40 TEST_NAME_KEY = 'test_name' | |
41 TEST_APK_KEY = 'test_apk_name' | |
42 CRBUG_KEY = 'bug_id' | |
43 CLASS_PATH_KEY = 'class_path' | |
44 CLASS_NAME_KEY = 'class_name' | |
45 TRACKED_TEST_COUNT_KEY = 'tracked_test_count' | |
46 DISABLED_TEST_COUNT_KEY = 'disabled_test_count' | |
47 FLAKY_TEST_COUNT_KEY = 'flaky_test_count' | |
48 TOTAL_TEST_COUNT_KEY = 'total_test_count' | |
49 UTC_BUILDTIME_KEY = 'build_time' | |
50 UTC_REVISIONTIME_KEY = 'revision_time' | |
51 PLATFORM_KEY = 'platform' | |
52 PLATFORM_VALUE = 'android' | |
53 | |
54 | |
55 def _GetBugId(message): | |
56 """Validate bug message format and get bug id""" | |
57 result = re.search(r'crbug(?:.com)?/(\d+)', message) | |
58 if result: | |
59 return int(result.group(1)) | |
60 else: | |
61 return None | |
62 | |
63 def _GetAnnotations(test_annotations, annotations_dict): | |
perezju
2016/04/25 12:45:08
don't pass annotations_dict; looks like it's alway
Yoland Yan(Google)
2016/04/26 01:10:08
Done.
| |
64 """Store annotations in the existing anntation_dict and return bug id""" | |
65 bug_id = None | |
66 for annotation, content in test_annotations.iteritems(): | |
67 if annotation in _EXCLUDE_ANNOTATION_LIST: | |
68 continue | |
69 if content is not None and content.get('message') is not None: | |
70 bug_id = _GetBugId(content.get('message')) | |
71 annotations_dict.update({annotation: content}) | |
72 return bug_id | |
73 | |
74 | |
75 def _GetTests(test_apks): | |
76 """All the all the tests""" | |
77 result = [] | |
78 total_test_count = 0 | |
79 for test_apk in test_apks: | |
80 logging.info('Current test apk: %s', test_apk) | |
81 test_jar = os.path.join( | |
82 constants.GetOutDirectory(), constants.SDK_BUILD_TEST_JAVALIB_DIR, | |
83 '%s.jar' % test_apk) | |
84 all_test = instrumentation_test_instance.GetAllTests(test_jar=test_jar) | |
perezju
2016/04/25 12:45:08
nit: all_tests
Yoland Yan(Google)
2016/04/26 01:10:08
Done.
| |
85 for test_class in all_test: | |
86 class_path = test_class['class'] | |
87 class_name = test_class['class'].split('.')[-1] | |
88 | |
89 class_annotation = {} | |
90 bug_id = _GetAnnotations(test_class['annotations'], class_annotation) | |
91 for test_method in test_class['methods']: | |
92 total_test_count += 1 | |
93 # getting annotation of each test case | |
94 test_annotations = {} | |
95 bug_id = _GetAnnotations(test_method['annotations'], test_annotations) | |
96 test_annotations.update(class_annotation) | |
97 # getting test method name of each test | |
98 test_name = test_method['method'] | |
99 test_dict = { | |
100 _JSONKeyName.CRBUG_KEY: bug_id, | |
101 _JSONKeyName.ANNOTATIONS_KEY: test_annotations, | |
102 _JSONKeyName.TEST_NAME_KEY: test_name, | |
103 _JSONKeyName.TEST_APK_KEY: test_apk, | |
104 _JSONKeyName.CLASS_NAME_KEY: class_name, | |
105 _JSONKeyName.CLASS_PATH_KEY: class_path | |
106 } | |
107 result.append(test_dict) | |
108 logging.info('Total count of tests in all test apks: %d', total_test_count) | |
109 return result, total_test_count | |
110 | |
111 | |
112 def _GetReportMeta(utc_buildtime_string, total_test_count): | |
113 """Returns a dictionary of the report's metadata""" | |
114 revision = cmd_helper.GetCmdOutput(['git', 'rev-parse', 'HEAD']).strip() | |
115 raw_string = cmd_helper.GetCmdOutput( | |
116 ['git', 'log', '--pretty=format:%aI', '--max-count=1', 'HEAD']) | |
117 time_string_search = re.search(r'\d+-\d+-\d+T\d+:\d+:\d+', raw_string) | |
perezju
2016/04/25 12:45:08
define a constant for this regex
Yoland Yan(Google)
2016/04/26 01:10:08
Done.
| |
118 if time_string_search is None: | |
119 raise Exception('Time format incorrect') | |
120 | |
121 raw_string = cmd_helper.GetCmdOutput( | |
122 ['git', 'log', '--pretty=format:%b', '--max-count=1', 'HEAD']) | |
123 commit_pos_search = re.search(r'Cr-Commit-Position: (.*)', raw_string) | |
perezju
2016/04/25 12:45:08
ditto
Yoland Yan(Google)
2016/04/26 01:10:08
Done.
| |
124 if commit_pos_search is None: | |
125 raise Exception('Cr commit position is not found, potentially running with ' | |
126 'uncommited HEAD') | |
127 commit_pos = commit_pos_search.group(1) | |
128 | |
129 revision_time = time.strptime(time_string_search.group(0), _GIT_TIME_FORMAT) | |
130 utc_revision_time = datetime.datetime.utcfromtimestamp( | |
131 time.mktime(revision_time)) | |
132 utc_revision_time = utc_revision_time.strftime(_EXPORT_TIME_FORMAT) | |
133 logging.info( | |
134 'revision is %s, revision time is %s', revision, utc_revision_time) | |
135 | |
136 record_data = { | |
137 _JSONKeyName.REVISION_KEY: revision, | |
138 _JSONKeyName.COMMIT_POS_KEY: commit_pos, | |
139 _JSONKeyName.UTC_BUILDTIME_KEY: utc_buildtime_string, | |
140 _JSONKeyName.UTC_REVISIONTIME_KEY: utc_revision_time, | |
141 _JSONKeyName.PLATFORM_KEY: _JSONKeyName.PLATFORM_VALUE, | |
142 _JSONKeyName.TOTAL_TEST_COUNT_KEY: total_test_count} | |
143 return record_data | |
perezju
2016/04/25 12:45:07
nit: just
return { ... }
Yoland Yan(Google)
2016/04/26 01:10:08
Done.
| |
144 | |
145 | |
146 def _GetReport(test_apks, buildtime_string): | |
147 """Generate the dictionary of report data | |
148 | |
149 Args: | |
150 test_apks: a list of apks for search for tests | |
151 buildtime_string: the time when the script is run at | |
152 format: '%Y%m%dT%H%M%S' | |
153 """ | |
154 | |
155 test_data, total_test_count = _GetTests(test_apks) | |
156 report_meta = _GetReportMeta(buildtime_string, total_test_count) | |
157 report_data = { | |
158 _JSONKeyName.REPORT_MASTER_KEY: report_meta, | |
159 _JSONKeyName.TEST_MASTER_KEY: test_data} | |
160 return report_data | |
161 | |
162 | |
163 def main(): | |
164 default_build_type = os.environ.get('BUILDTYPE', 'Debug') | |
165 parser = argparse.ArgumentParser() | |
166 parser.add_argument('-t', '--test-apks', nargs='+', dest='test_apks', | |
167 help='List all test apks file name that the script uses ' | |
168 'to fetch tracked tests from') | |
169 parser.add_argument( | |
170 '--debug', action='store_const', const='Debug', dest='build_type', | |
171 default=default_build_type, | |
172 help=('If set, run test suites under out/Debug. ' | |
173 'Default is env var BUILDTYPE or Debug.')) | |
174 parser.add_argument( | |
175 '--release', action='store_const', const='Release', dest='build_type', | |
176 help=('If set, run test suites under out/Release. ' | |
177 'Default is env var BUILDTYPE or Debug.')) | |
178 parser.add_argument('-o', '--output-path', | |
179 help='JSON file output to be uploaded on to gcs') | |
180 parser.add_argument('-v', '--verbose', action='store_true', default=False, | |
181 help='DEBUG verbosity') | |
182 | |
183 arguments = parser.parse_args(sys.argv[1:]) | |
184 logging.basicConfig( | |
185 level=logging.DEBUG if arguments.verbose else logging.WARNING) | |
186 constants.SetBuildType(arguments.build_type) | |
187 logging.info('Using jar from build type: %s', arguments.build_type) | |
188 | |
189 buildtime = datetime.datetime.utcnow() | |
190 buildtime_string = buildtime.strftime(_EXPORT_TIME_FORMAT) | |
191 logging.info('Build time is %s', buildtime_string) | |
192 report_data = _GetReport(arguments.test_apks, buildtime_string) | |
193 | |
194 if arguments.output_path is None: | |
195 output_path = constants.GetOutDirectory() | |
196 else: | |
197 output_path = arguments.output_path | |
198 json_output_path = os.path.join(output_path, | |
199 '%s-android-chrome.json' % buildtime_string) | |
200 with open(json_output_path, 'w') as f: | |
201 json.dump(report_data, f, indent=2, sort_keys=True, separators=(',',':')) | |
perezju
2016/04/25 12:45:08
':' -> ': ' (missing a space)
Yoland Yan(Google)
2016/04/26 01:10:08
woah, done!
| |
202 logging.info('Saved json output file to %s', json_output_path) | |
203 | |
204 if __name__ == '__main__': | |
205 main() | |
OLD | NEW |