Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/env python | |
| 2 # Copyright 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 annotated 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 from pylib import constants | |
| 24 from pylib.instrumentation import instrumentation_test_instance | |
| 25 | |
| 26 sys.path.append(os.path.join(_SRC_DIR, 'third_party', 'catapult', 'devil')) | |
|
jbudorick
2016/06/11 00:24:02
nit: devil should precede pylib in the import list
the real yoland
2016/06/11 01:35:43
Done
| |
| 27 from devil.utils import cmd_helper | |
| 28 | |
| 29 _GIT_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S' | |
|
jbudorick
2016/06/11 00:24:03
nit: alphabetize these constants within each subgr
the real yoland
2016/06/11 01:35:43
Done
| |
| 30 _EXPORT_TIME_FORMAT = '%Y%m%dT%H%M%S' | |
| 31 | |
| 32 _GIT_LOG_TIME_PATTERN = re.compile(r'\d+') | |
| 33 _GIT_LOG_MESSAGE_PATTERN = r'Cr-Commit-Position: refs/heads/master@{#(\d+)}' | |
| 34 | |
| 35 _CRBUG_ID_PATTERN = re.compile(r'crbug(?:.com)?/(\d+)') | |
| 36 | |
| 37 _TEST_MASTER_KEY = 'tests' | |
|
jbudorick
2016/06/11 00:24:03
Are all of these only used once? If so, why are th
the real yoland
2016/06/11 01:35:43
I was using them here so if any renaming is decide
| |
| 38 _REPORT_MASTER_KEY = 'metadata' | |
| 39 _REVISION_KEY = 'revision' | |
| 40 _COMMIT_POS_KEY = 'commit_pos' | |
| 41 _ANNOTATIONS_KEY = 'annotations' | |
| 42 _TEST_NAME_KEY = 'test_name' | |
| 43 _TEST_APK_KEY = 'test_apk_name' | |
| 44 _CRBUG_KEY = 'bug_id' | |
| 45 _CLASS_PATH_KEY = 'class_path' | |
| 46 _CLASS_NAME_KEY = 'class_name' | |
| 47 _TOTAL_TEST_COUNT_KEY = 'total_test_count' | |
| 48 _UTC_SCRIPT_RUNTIME= 'script_runtime' | |
| 49 _UTC_REVISIONTIME_KEY = 'revision_time' | |
| 50 _PLATFORM_KEY = 'platform' | |
| 51 _PLATFORM_VALUE = 'android' | |
| 52 | |
| 53 | |
| 54 def _GetBugId(message): | |
| 55 """Validate bug message format and get bug id""" | |
| 56 result = re.search(_CRBUG_ID_PATTERN, message) | |
| 57 if result: | |
| 58 return int(result.group(1)) | |
| 59 else: | |
| 60 return None | |
| 61 | |
| 62 | |
| 63 def _GetAnnotations(test_annotations): | |
| 64 """Store annotations in the existing anntation_dict and return bug id""" | |
|
jbudorick
2016/06/11 00:24:03
nit: anntation_dict -> annotation_dict
the real yoland
2016/06/11 01:35:43
...O_o how
Done
| |
| 65 bug_id = None | |
| 66 annotations_dict = {} | |
|
jbudorick
2016/06/11 00:24:03
What's the point of annotations_dict? It's the sam
the real yoland
2016/06/11 01:35:43
My bad, inherited problem from previous CLs
Done
| |
| 67 for annotation, content in test_annotations.iteritems(): | |
| 68 if content is not None and content.get('message') is not None: | |
|
jbudorick
2016/06/11 00:24:03
nit: collapse these two lines down to just
bug_id
the real yoland
2016/06/11 01:35:43
Done
| |
| 69 bug_id = _GetBugId(content.get('message')) | |
|
jbudorick
2016/06/11 00:24:03
So this gets any annotation with a crbug message?
the real yoland
2016/06/11 01:35:43
Yes, it parses any annotation with message to matc
| |
| 70 annotations_dict.update({annotation: content}) | |
| 71 return bug_id, annotations_dict | |
| 72 | |
| 73 | |
| 74 def _GetTests(test_apks, apk_output_dir): | |
| 75 """Return a list of all annotated tests and total test count""" | |
| 76 result = [] | |
| 77 total_test_count = 0 | |
| 78 for test_apk in test_apks: | |
| 79 logging.info('Current test apk: %s', test_apk) | |
| 80 test_jar = os.path.join( | |
| 81 apk_output_dir, constants.SDK_BUILD_TEST_JAVALIB_DIR, | |
| 82 '%s.jar' % test_apk) | |
| 83 all_tests = instrumentation_test_instance.GetAllTests(test_jar=test_jar) | |
| 84 for test_class in all_tests: | |
| 85 class_path = test_class['class'] | |
| 86 class_name = test_class['class'].split('.')[-1] | |
| 87 | |
| 88 class_bug_id, class_annotation = _GetAnnotations( | |
| 89 test_class['annotations']) | |
| 90 for test_method in test_class['methods']: | |
| 91 total_test_count += 1 | |
| 92 # getting annotation of each test case | |
| 93 test_bug_id, test_annotations = _GetAnnotations( | |
| 94 test_method['annotations']) | |
| 95 test_bug_id = test_bug_id if test_bug_id else class_bug_id | |
| 96 test_annotations.update(class_annotation) | |
| 97 # getting test method name of each test | |
| 98 test_name = test_method['method'] | |
| 99 test_dict = { | |
| 100 _CRBUG_KEY: test_bug_id, | |
| 101 _ANNOTATIONS_KEY: test_annotations, | |
| 102 _TEST_NAME_KEY: test_name, | |
| 103 _TEST_APK_KEY: test_apk, | |
| 104 _CLASS_NAME_KEY: class_name, | |
| 105 _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_script_runtime_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:%at', '--max-count=1', 'HEAD']) | |
| 117 time_string_search = re.search(_GIT_LOG_TIME_PATTERN, raw_string) | |
| 118 if time_string_search is None: | |
| 119 raise Exception('Timestamp format incorrect, expected all digits, got %s' | |
| 120 % raw_string) | |
| 121 | |
| 122 raw_string = cmd_helper.GetCmdOutput( | |
| 123 ['git', 'log', '--pretty=format:%b', '--max-count=1', 'HEAD']) | |
| 124 commit_pos_search = re.search(_GIT_LOG_MESSAGE_PATTERN, raw_string) | |
| 125 if commit_pos_search is None: | |
| 126 raise Exception('Cr-Commit-Position is not found, potentially running with ' | |
| 127 'uncommited HEAD') | |
| 128 commit_pos = int(commit_pos_search.group(1)) | |
| 129 | |
| 130 utc_revision_time = datetime.datetime.utcfromtimestamp( | |
| 131 int(time_string_search.group(0))) | |
| 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 return { | |
| 137 _REVISION_KEY: revision, | |
| 138 _COMMIT_POS_KEY: commit_pos, | |
| 139 _UTC_SCRIPT_RUNTIME: utc_script_runtime_string, | |
| 140 _UTC_REVISIONTIME_KEY: utc_revision_time, | |
| 141 _PLATFORM_KEY: _PLATFORM_VALUE, | |
| 142 _TOTAL_TEST_COUNT_KEY: total_test_count | |
| 143 } | |
| 144 | |
| 145 | |
| 146 def _GetReport(test_apks, script_runtime_string, apk_output_dir): | |
| 147 """Generate the dictionary of report data | |
| 148 | |
| 149 Args: | |
| 150 test_apks: a list of apks for search for tests | |
| 151 script_runtime_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, apk_output_dir) | |
| 156 report_meta = _GetReportMeta(script_runtime_string, total_test_count) | |
| 157 report_data = { | |
| 158 _REPORT_MASTER_KEY: report_meta, | |
| 159 _TEST_MASTER_KEY: test_data} | |
|
jbudorick
2016/06/11 00:24:02
nit: drop } onto its own line
the real yoland
2016/06/11 01:35:43
Done
| |
| 160 return report_data | |
| 161 | |
| 162 | |
| 163 def main(): | |
| 164 parser = argparse.ArgumentParser() | |
| 165 parser.add_argument('-t', '--test-apks', nargs='+', dest='test_apks', | |
| 166 required=True, | |
| 167 help='List all test apks file name that the script uses ' | |
| 168 'to fetch tracked tests from') | |
| 169 parser.add_argument('--json-output-dir', required=True, | |
| 170 help='JSON file output directory') | |
| 171 parser.add_argument('--apk-output-dir', required=True, | |
| 172 help='The output directory of test apks') | |
| 173 parser.add_argument('-v', '--verbose', action='store_true', default=False, | |
| 174 help='INFO verbosity') | |
| 175 | |
| 176 arguments = parser.parse_args(sys.argv[1:]) | |
| 177 logging.basicConfig( | |
| 178 level=logging.INFO if arguments.verbose else logging.WARNING) | |
| 179 | |
| 180 script_runtime = datetime.datetime.utcnow() | |
| 181 script_runtime_string = script_runtime.strftime(_EXPORT_TIME_FORMAT) | |
| 182 logging.info('Build time is %s', script_runtime_string) | |
| 183 apk_output_dir = os.path.abspath(os.path.join( | |
| 184 constants.DIR_SOURCE_ROOT, arguments.apk_output_dir)) | |
|
jbudorick
2016/06/11 00:24:03
nit: indent two spaces
the real yoland
2016/06/11 01:35:43
Done
| |
| 185 report_data = _GetReport( | |
| 186 arguments.test_apks, script_runtime_string, apk_output_dir) | |
| 187 | |
| 188 temp_output_dir = arguments.json_output_dir | |
| 189 json_output_path = os.path.join( | |
| 190 temp_output_dir, '%s-android-chrome.json' % script_runtime_string) | |
| 191 with open(json_output_path, 'w') as f: | |
| 192 json.dump(report_data, f, sort_keys=True, separators=(',',': ')) | |
| 193 logging.info('Saved json output file to %s', json_output_path) | |
| 194 | |
| 195 if __name__ == '__main__': | |
| 196 main() | |
| OLD | NEW |