| Index: tools/android/find_annotated_tests.py
|
| diff --git a/tools/android/find_annotated_tests.py b/tools/android/find_annotated_tests.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..c6b966238992af914737950163e46d0a3177c1d4
|
| --- /dev/null
|
| +++ b/tools/android/find_annotated_tests.py
|
| @@ -0,0 +1,181 @@
|
| +#!/usr/bin/env python
|
| +# Copyright 2016 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +"""Finds all the annotated tests from proguard dump"""
|
| +
|
| +import argparse
|
| +import datetime
|
| +import json
|
| +import linecache
|
| +import logging
|
| +import os
|
| +import pprint
|
| +import re
|
| +import sys
|
| +import time
|
| +
|
| +_SRC_DIR = os.path.abspath(os.path.join(
|
| + os.path.dirname(__file__), '..', '..'))
|
| +
|
| +sys.path.append(os.path.join(_SRC_DIR, 'third_party', 'catapult', 'devil'))
|
| +from devil.utils import cmd_helper
|
| +
|
| +sys.path.append(os.path.join(_SRC_DIR, 'build', 'android'))
|
| +from pylib import constants
|
| +from pylib.instrumentation import instrumentation_test_instance
|
| +
|
| +
|
| +_CRBUG_ID_PATTERN = re.compile(r'crbug(?:.com)?/(\d+)')
|
| +_EXPORT_TIME_FORMAT = '%Y%m%dT%H%M%S'
|
| +_GIT_LOG_TIME_PATTERN = re.compile(r'\d+')
|
| +_GIT_LOG_MESSAGE_PATTERN = r'Cr-Commit-Position: refs/heads/master@{#(\d+)}'
|
| +_GIT_TIME_FORMAT = '%Y-%m-%dT%H:%M:%S'
|
| +
|
| +
|
| +def _GetBugId(test_annotations):
|
| + """Find and return the test bug id from its annoation message elements"""
|
| + # TODO(yolandyan): currently the script only supports on bug id per method,
|
| + # add support for multiple bug id
|
| + for content in test_annotations.itervalues():
|
| + if content and content.get('message'):
|
| + search_result = re.search(_CRBUG_ID_PATTERN, content.get('message'))
|
| + if search_result is not None:
|
| + return int(search_result.group(1))
|
| + return None
|
| +
|
| +
|
| +def _GetTests(test_apks, apk_output_dir):
|
| + """Return a list of all annotated tests and total test count"""
|
| + result = []
|
| + total_test_count = 0
|
| + for test_apk in test_apks:
|
| + logging.info('Current test apk: %s', test_apk)
|
| + test_jar = os.path.join(
|
| + apk_output_dir, constants.SDK_BUILD_TEST_JAVALIB_DIR,
|
| + '%s.jar' % test_apk)
|
| + all_tests = instrumentation_test_instance.GetAllTests(test_jar=test_jar)
|
| + for test_class in all_tests:
|
| + class_path = test_class['class']
|
| + class_name = test_class['class'].split('.')[-1]
|
| +
|
| + class_annotations = test_class['annotations']
|
| + class_bug_id = _GetBugId(class_annotations)
|
| + for test_method in test_class['methods']:
|
| + total_test_count += 1
|
| + # getting annotation of each test case
|
| + test_annotations = test_method['annotations']
|
| + test_bug_id = _GetBugId(test_annotations)
|
| + test_bug_id = test_bug_id if test_bug_id else class_bug_id
|
| + test_annotations.update(class_annotations)
|
| + # getting test method name of each test
|
| + test_name = test_method['method']
|
| + test_dict = {
|
| + 'bug_id': test_bug_id,
|
| + 'annotations': test_annotations,
|
| + 'test_name': test_name,
|
| + 'test_apk_name': test_apk,
|
| + 'class_name': class_name,
|
| + 'class_path': class_path
|
| + }
|
| + result.append(test_dict)
|
| +
|
| + logging.info('Total count of tests in all test apks: %d', total_test_count)
|
| + return result, total_test_count
|
| +
|
| +
|
| +def _GetReportMeta(utc_script_runtime_string, total_test_count):
|
| + """Returns a dictionary of the report's metadata"""
|
| + revision = cmd_helper.GetCmdOutput(['git', 'rev-parse', 'HEAD']).strip()
|
| + raw_string = cmd_helper.GetCmdOutput(
|
| + ['git', 'log', '--pretty=format:%at', '--max-count=1', 'HEAD'])
|
| + time_string_search = re.search(_GIT_LOG_TIME_PATTERN, raw_string)
|
| + if time_string_search is None:
|
| + raise Exception('Timestamp format incorrect, expected all digits, got %s'
|
| + % raw_string)
|
| +
|
| + raw_string = cmd_helper.GetCmdOutput(
|
| + ['git', 'log', '--pretty=format:%b', '--max-count=1', 'HEAD'])
|
| + commit_pos_search = re.search(_GIT_LOG_MESSAGE_PATTERN, raw_string)
|
| + if commit_pos_search is None:
|
| + raise Exception('Cr-Commit-Position is not found, potentially running with '
|
| + 'uncommited HEAD')
|
| + commit_pos = int(commit_pos_search.group(1))
|
| +
|
| + utc_revision_time = datetime.datetime.utcfromtimestamp(
|
| + int(time_string_search.group(0)))
|
| + utc_revision_time = utc_revision_time.strftime(_EXPORT_TIME_FORMAT)
|
| + logging.info(
|
| + 'revision is %s, revision time is %s', revision, utc_revision_time)
|
| +
|
| + return {
|
| + 'revision': revision,
|
| + 'commit_pos': commit_pos,
|
| + 'script_runtime': utc_script_runtime_string,
|
| + 'revision_time': utc_revision_time,
|
| + 'platform': 'android',
|
| + 'total_test_count': total_test_count
|
| + }
|
| +
|
| +
|
| +def _GetReport(test_apks, script_runtime_string, apk_output_dir):
|
| + """Generate the dictionary of report data
|
| +
|
| + Args:
|
| + test_apks: a list of apks for search for tests
|
| + script_runtime_string: the time when the script is run at
|
| + format: '%Y%m%dT%H%M%S'
|
| + """
|
| +
|
| + test_data, total_test_count = _GetTests(test_apks, apk_output_dir)
|
| + report_meta = _GetReportMeta(script_runtime_string, total_test_count)
|
| + report_data = {
|
| + 'metadata': report_meta,
|
| + 'tests': test_data
|
| + }
|
| + return report_data
|
| +
|
| +
|
| +def main():
|
| + parser = argparse.ArgumentParser()
|
| + parser.add_argument('-t', '--test-apks', nargs='+', dest='test_apks',
|
| + required=True,
|
| + help='List all test apks file name that the script uses '
|
| + 'to fetch tracked tests from')
|
| + parser.add_argument('--json-output-dir', required=True,
|
| + help='JSON file output dir')
|
| + parser.add_argument('--apk-output-dir', required=True,
|
| + help='The output directory of test apks')
|
| + parser.add_argument('--timestamp-string',
|
| + help='The time when this script is run, passed in by the '
|
| + 'recipe that runs this script so both the recipe '
|
| + 'and this script use it to format output json name')
|
| + parser.add_argument('-v', '--verbose', action='store_true', default=False,
|
| + help='INFO verbosity')
|
| +
|
| + arguments = parser.parse_args(sys.argv[1:])
|
| + logging.basicConfig(
|
| + level=logging.INFO if arguments.verbose else logging.WARNING)
|
| +
|
| + if arguments.timestamp_string is None:
|
| + script_runtime = datetime.datetime.utcnow()
|
| + script_runtime_string = script_runtime.strftime(_EXPORT_TIME_FORMAT)
|
| + else:
|
| + script_runtime = arguments.timestamp_string
|
| + logging.info('Build time is %s', script_runtime_string)
|
| + apk_output_dir = os.path.abspath(os.path.join(
|
| + constants.DIR_SOURCE_ROOT, arguments.apk_output_dir))
|
| + report_data = _GetReport(
|
| + arguments.test_apks, script_runtime_string, apk_output_dir)
|
| +
|
| + json_output_path = os.path.join(
|
| + arguments.json_output_dir,
|
| + '%s-android-chrome.json' % script_runtime_string)
|
| + with open(json_output_path, 'w') as f:
|
| + json.dump(report_data, f, sort_keys=True, separators=(',',': '))
|
| + logging.info('Saved json output file to %s', json_output_path)
|
| +
|
| +
|
| +if __name__ == '__main__':
|
| + sys.exit(main())
|
|
|