Chromium Code Reviews| 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 |