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, 'third_party', 'catapult', 'devil')) |
| 23 from devil.utils import cmd_helper |
| 24 |
| 25 sys.path.append(os.path.join(_SRC_DIR, 'build', 'android')) |
| 26 from pylib import constants |
| 27 from pylib.instrumentation import instrumentation_test_instance |
| 28 |
| 29 |
| 30 _CRBUG_ID_PATTERN = re.compile(r'crbug(?:.com)?/(\d+)') |
| 31 _EXPORT_TIME_FORMAT = '%Y%m%dT%H%M%S' |
| 32 _GIT_LOG_TIME_PATTERN = re.compile(r'\d+') |
| 33 _GIT_LOG_MESSAGE_PATTERN = r'Cr-Commit-Position: refs/heads/master@{#(\d+)}' |
| 34 _GIT_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S' |
| 35 |
| 36 |
| 37 def _GetBugId(test_annotations): |
| 38 """Find and return the test bug id from its annoation message elements""" |
| 39 # TODO(yolandyan): currently the script only supports on bug id per method, |
| 40 # add support for multiple bug id |
| 41 for content in test_annotations.itervalues(): |
| 42 if content and content.get('message'): |
| 43 search_result = re.search(_CRBUG_ID_PATTERN, content.get('message')) |
| 44 if search_result is not None: |
| 45 return int(search_result.group(1)) |
| 46 return None |
| 47 |
| 48 |
| 49 def _GetTests(test_apks, apk_output_dir): |
| 50 """Return a list of all annotated tests and total test count""" |
| 51 result = [] |
| 52 total_test_count = 0 |
| 53 for test_apk in test_apks: |
| 54 logging.info('Current test apk: %s', test_apk) |
| 55 test_jar = os.path.join( |
| 56 apk_output_dir, constants.SDK_BUILD_TEST_JAVALIB_DIR, |
| 57 '%s.jar' % test_apk) |
| 58 all_tests = instrumentation_test_instance.GetAllTests(test_jar=test_jar) |
| 59 for test_class in all_tests: |
| 60 class_path = test_class['class'] |
| 61 class_name = test_class['class'].split('.')[-1] |
| 62 |
| 63 class_annotations = test_class['annotations'] |
| 64 class_bug_id = _GetBugId(class_annotations) |
| 65 for test_method in test_class['methods']: |
| 66 total_test_count += 1 |
| 67 # getting annotation of each test case |
| 68 test_annotations = test_method['annotations'] |
| 69 test_bug_id = _GetBugId(test_annotations) |
| 70 test_bug_id = test_bug_id if test_bug_id else class_bug_id |
| 71 test_annotations.update(class_annotations) |
| 72 # getting test method name of each test |
| 73 test_name = test_method['method'] |
| 74 test_dict = { |
| 75 'bug_id': test_bug_id, |
| 76 'annotations': test_annotations, |
| 77 'test_name': test_name, |
| 78 'test_apk_name': test_apk, |
| 79 'class_name': class_name, |
| 80 'class_path': class_path |
| 81 } |
| 82 result.append(test_dict) |
| 83 |
| 84 logging.info('Total count of tests in all test apks: %d', total_test_count) |
| 85 return result, total_test_count |
| 86 |
| 87 |
| 88 def _GetReportMeta(utc_script_runtime_string, total_test_count): |
| 89 """Returns a dictionary of the report's metadata""" |
| 90 revision = cmd_helper.GetCmdOutput(['git', 'rev-parse', 'HEAD']).strip() |
| 91 raw_string = cmd_helper.GetCmdOutput( |
| 92 ['git', 'log', '--pretty=format:%at', '--max-count=1', 'HEAD']) |
| 93 time_string_search = re.search(_GIT_LOG_TIME_PATTERN, raw_string) |
| 94 if time_string_search is None: |
| 95 raise Exception('Timestamp format incorrect, expected all digits, got %s' |
| 96 % raw_string) |
| 97 |
| 98 raw_string = cmd_helper.GetCmdOutput( |
| 99 ['git', 'log', '--pretty=format:%b', '--max-count=1', 'HEAD']) |
| 100 commit_pos_search = re.search(_GIT_LOG_MESSAGE_PATTERN, raw_string) |
| 101 if commit_pos_search is None: |
| 102 raise Exception('Cr-Commit-Position is not found, potentially running with ' |
| 103 'uncommited HEAD') |
| 104 commit_pos = int(commit_pos_search.group(1)) |
| 105 |
| 106 utc_revision_time = datetime.datetime.utcfromtimestamp( |
| 107 int(time_string_search.group(0))) |
| 108 utc_revision_time = utc_revision_time.strftime(_EXPORT_TIME_FORMAT) |
| 109 logging.info( |
| 110 'revision is %s, revision time is %s', revision, utc_revision_time) |
| 111 |
| 112 return { |
| 113 'revision': revision, |
| 114 'commit_pos': commit_pos, |
| 115 'script_runtime': utc_script_runtime_string, |
| 116 'revision_time': utc_revision_time, |
| 117 'platform': 'android', |
| 118 'total_test_count': total_test_count |
| 119 } |
| 120 |
| 121 |
| 122 def _GetReport(test_apks, script_runtime_string, apk_output_dir): |
| 123 """Generate the dictionary of report data |
| 124 |
| 125 Args: |
| 126 test_apks: a list of apks for search for tests |
| 127 script_runtime_string: the time when the script is run at |
| 128 format: '%Y%m%dT%H%M%S' |
| 129 """ |
| 130 |
| 131 test_data, total_test_count = _GetTests(test_apks, apk_output_dir) |
| 132 report_meta = _GetReportMeta(script_runtime_string, total_test_count) |
| 133 report_data = { |
| 134 'metadata': report_meta, |
| 135 'tests': test_data |
| 136 } |
| 137 return report_data |
| 138 |
| 139 |
| 140 def main(): |
| 141 parser = argparse.ArgumentParser() |
| 142 parser.add_argument('-t', '--test-apks', nargs='+', dest='test_apks', |
| 143 required=True, |
| 144 help='List all test apks file name that the script uses ' |
| 145 'to fetch tracked tests from') |
| 146 parser.add_argument('--json-output-dir', required=True, |
| 147 help='JSON file output dir') |
| 148 parser.add_argument('--apk-output-dir', required=True, |
| 149 help='The output directory of test apks') |
| 150 parser.add_argument('--timestamp-string', |
| 151 help='The time when this script is run, passed in by the ' |
| 152 'recipe that runs this script so both the recipe ' |
| 153 'and this script use it to format output json name') |
| 154 parser.add_argument('-v', '--verbose', action='store_true', default=False, |
| 155 help='INFO verbosity') |
| 156 |
| 157 arguments = parser.parse_args(sys.argv[1:]) |
| 158 logging.basicConfig( |
| 159 level=logging.INFO if arguments.verbose else logging.WARNING) |
| 160 |
| 161 if arguments.timestamp_string is None: |
| 162 script_runtime = datetime.datetime.utcnow() |
| 163 script_runtime_string = script_runtime.strftime(_EXPORT_TIME_FORMAT) |
| 164 else: |
| 165 script_runtime = arguments.timestamp_string |
| 166 logging.info('Build time is %s', script_runtime_string) |
| 167 apk_output_dir = os.path.abspath(os.path.join( |
| 168 constants.DIR_SOURCE_ROOT, arguments.apk_output_dir)) |
| 169 report_data = _GetReport( |
| 170 arguments.test_apks, script_runtime_string, apk_output_dir) |
| 171 |
| 172 json_output_path = os.path.join( |
| 173 arguments.json_output_dir, |
| 174 '%s-android-chrome.json' % script_runtime_string) |
| 175 with open(json_output_path, 'w') as f: |
| 176 json.dump(report_data, f, sort_keys=True, separators=(',',': ')) |
| 177 logging.info('Saved json output file to %s', json_output_path) |
| 178 |
| 179 |
| 180 if __name__ == '__main__': |
| 181 sys.exit(main()) |
OLD | NEW |