blob: 3862b46622d6c067ee6eb17fd94ec64656de3dfb [file] [log] [blame]
mikecase5c5e3f02016-12-14 21:54:211#!/usr/bin/env python
2#
3# Copyright 2016 The Chromium Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
Andrew Luo96e2fef8d2018-08-22 20:18:247"""Runs the CTS test APKs stored in CIPD."""
mikecase5c5e3f02016-12-14 21:54:218
9import argparse
10import json
Andrew Luo96e2fef8d2018-08-22 20:18:2411import logging
mikecase5c5e3f02016-12-14 21:54:2112import os
13import shutil
14import sys
15import tempfile
Andrew Luo8a4f19ed2018-04-19 20:07:4716import zipfile
mikecase5c5e3f02016-12-14 21:54:2117
18
19sys.path.append(os.path.join(
20 os.path.dirname(__file__), os.pardir, os.pardir, 'build', 'android'))
21import devil_chromium # pylint: disable=import-error
Andrew Luo96e2fef8d2018-08-22 20:18:2422from devil.android.sdk import version_codes # pylint: disable=import-error
23from devil.android.tools import script_common # pylint: disable=import-error
mikecase5c5e3f02016-12-14 21:54:2124from devil.utils import cmd_helper # pylint: disable=import-error
25
Andrew Luo8a4f19ed2018-04-19 20:07:4726# cts test archives for all platforms are stored in this bucket
27# contents need to be updated if there is an important fix to any of
28# the tests
mikecase5c5e3f02016-12-14 21:54:2129
mikecase5c5e3f02016-12-14 21:54:2130_TEST_RUNNER_PATH = os.path.join(
31 os.path.dirname(__file__), os.pardir, os.pardir,
32 'build', 'android', 'test_runner.py')
33
34_EXPECTED_FAILURES_FILE = os.path.join(
35 os.path.dirname(__file__), 'cts_config', 'expected_failure_on_bot.json')
36_WEBVIEW_CTS_GCS_PATH_FILE = os.path.join(
37 os.path.dirname(__file__), 'cts_config', 'webview_cts_gcs_path.json')
Andrew Luo96e2fef8d2018-08-22 20:18:2438_CTS_ARCHIVE_DIR = os.path.join(os.path.dirname(__file__), 'cts_archive')
39
40_SDK_PLATFORM_DICT = {
41 version_codes.LOLLIPOP: 'L',
42 version_codes.LOLLIPOP_MR1: 'L',
43 version_codes.MARSHMALLOW: 'M',
44 version_codes.NOUGAT: 'N',
45 version_codes.NOUGAT_MR1: 'N',
46 version_codes.OREO: 'O',
47 version_codes.OREO_MR1: 'O'
48}
mikecase5c5e3f02016-12-14 21:54:2149
50
Andrew Luo8a4f19ed2018-04-19 20:07:4751def GetCtsInfo(arch, platform, item):
52 """Gets contents of CTS Info for arch and platform.
53
54 See _WEBVIEW_CTS_GCS_PATH_FILE
55 """
mikecase5c5e3f02016-12-14 21:54:2156 with open(_WEBVIEW_CTS_GCS_PATH_FILE) as f:
57 cts_gcs_path_info = json.load(f)
58 try:
Andrew Luo8a4f19ed2018-04-19 20:07:4759 return cts_gcs_path_info[arch][platform][item]
mikecase5c5e3f02016-12-14 21:54:2160 except KeyError:
Andrew Luo8a4f19ed2018-04-19 20:07:4761 raise Exception('No %s info available for arch:%s, android:%s' %
62 (item, arch, platform))
mikecase5c5e3f02016-12-14 21:54:2163
64
65def GetExpectedFailures():
Andrew Luo8a4f19ed2018-04-19 20:07:4766 """Gets list of tests expected to fail in <class>#<method> format.
67
68 See _EXPECTED_FAILURES_FILE
69 """
mikecase5c5e3f02016-12-14 21:54:2170 with open(_EXPECTED_FAILURES_FILE) as f:
71 expected_failures_info = json.load(f)
72 expected_failures = []
73 for class_name, methods in expected_failures_info.iteritems():
74 expected_failures.extend(['%s#%s' % (class_name, m['name'])
75 for m in methods])
76 return expected_failures
77
Andrew Luo6a84e612018-11-08 04:44:4478def GetTestRunFilterArg(test_run, skip_expected_failures):
79 skips = []
80 if skip_expected_failures:
81 skips = GetExpectedFailures()
mikecase5c5e3f02016-12-14 21:54:2182
Andrew Luo6a84e612018-11-08 04:44:4483 excludes = test_run.get("excludes", [])
84 includes = test_run.get("includes", [])
85 assert len(excludes) == 0 or len(includes) == 0, \
86 "test_runs error, can't have both includes and excludes: %s" % test_run
87 if len(includes) > 0:
88 return ['-f=' + ':'.join([i["match"] for i in includes])]
89 else:
90 skips.extend([i["match"] for i in excludes])
91 if len(skips) > 0:
92 return ['-f=' + "-" + ':'.join(skips)]
93 return []
94
95def RunCTS(test_runner_args, local_cts_dir, test_run,
Andrew Luo8a4f19ed2018-04-19 20:07:4796 skip_expected_failures=True, json_results_file=None):
97 """Run tests in apk using test_runner script at _TEST_RUNNER_PATH.
98
99 Returns the script result code,
100 tests expected to fail will be skipped unless skip_expected_failures
101 is set to False, test results will be stored in
102 the json_results_file file if specified
103 """
Andrew Luo6a84e612018-11-08 04:44:44104
105 apk = test_run['apk']
106
Andrew Luo8a4f19ed2018-04-19 20:07:47107 local_test_runner_args = test_runner_args + ['--test-apk',
108 os.path.join(local_cts_dir, apk)]
109
110 # TODO(mikecase): This doesn't work at all with the
111 # --gtest-filter test runner option currently. The
112 # filter options will just override eachother.
Andrew Luo8a4f19ed2018-04-19 20:07:47113 # The preferred method is to specify test filters per release in
114 # the CTS_GCS path file. It will override any
115 # previous filters, including ones in expected failures
116 # file.
Andrew Luo6a84e612018-11-08 04:44:44117 local_test_runner_args.extend(GetTestRunFilterArg(test_run,
118 skip_expected_failures))
119
Andrew Luo8a4f19ed2018-04-19 20:07:47120 if json_results_file:
121 local_test_runner_args += ['--json-results-file=%s' %
122 json_results_file]
123 return cmd_helper.RunCmd(
124 [_TEST_RUNNER_PATH, 'instrumentation'] + local_test_runner_args)
125
126
127def MergeTestResults(existing_results_json, additional_results_json):
128 """Appends results in additional_results_json to existing_results_json."""
129 for k, v in additional_results_json.iteritems():
130 if k not in existing_results_json:
131 existing_results_json[k] = v
132 else:
Ian Vollick2698a262018-07-31 13:59:57133 if isinstance(v, dict):
134 if not isinstance(existing_results_json[k], dict):
135 raise NotImplementedError(
136 "Can't merge results field %s of different types" % v)
Andrew Luo8a4f19ed2018-04-19 20:07:47137 existing_results_json[k].update(v)
Ian Vollick2698a262018-07-31 13:59:57138 elif isinstance(v, list):
139 if not isinstance(existing_results_json[k], list):
140 raise NotImplementedError(
141 "Can't merge results field %s of different types" % v)
Andrew Luo8a4f19ed2018-04-19 20:07:47142 existing_results_json[k].extend(v)
143 else:
144 raise NotImplementedError(
145 "Can't merge results field %s that is not a list or dict" % v)
146
147
Andrew Luo96e2fef8d2018-08-22 20:18:24148def ExtractCTSZip(args):
149 """Extract the CTS tests for args.platform.
Andrew Luo8a4f19ed2018-04-19 20:07:47150
Andrew Luo96e2fef8d2018-08-22 20:18:24151 Extract the CTS zip file from _CTS_ARCHIVE_DIR to
Andrew Luo8a4f19ed2018-04-19 20:07:47152 apk_dir if specified, or a new temporary directory if not.
153 Returns following tuple (local_cts_dir, base_cts_dir, delete_cts_dir):
154 local_cts_dir - CTS extraction location for current arch and platform
155 base_cts_dir - Root directory for all the arches and platforms
156 delete_cts_dir - Set if the base_cts_dir was created as a temporary
157 directory
158 """
mikecase5c5e3f02016-12-14 21:54:21159 base_cts_dir = None
160 delete_cts_dir = False
Andrew Luo8a4f19ed2018-04-19 20:07:47161 relative_cts_zip_path = GetCtsInfo(args.arch, args.platform, 'filename')
162
163 if args.apk_dir:
164 base_cts_dir = args.apk_dir
165 else:
166 base_cts_dir = tempfile.mkdtemp()
167 delete_cts_dir = True
168
Andrew Luo96e2fef8d2018-08-22 20:18:24169 cts_zip_path = os.path.join(_CTS_ARCHIVE_DIR, relative_cts_zip_path)
Andrew Luo8a4f19ed2018-04-19 20:07:47170 local_cts_dir = os.path.join(base_cts_dir,
Andrew Luo6a84e612018-11-08 04:44:44171 GetCtsInfo(args.arch, args.platform,
172 'unzip_dir')
173 )
Andrew Luo96e2fef8d2018-08-22 20:18:24174 zf = zipfile.ZipFile(cts_zip_path, 'r')
Andrew Luo8a4f19ed2018-04-19 20:07:47175 zf.extractall(local_cts_dir)
176 return (local_cts_dir, base_cts_dir, delete_cts_dir)
177
178
Andrew Luo96e2fef8d2018-08-22 20:18:24179def RunAllCTSTests(args, test_runner_args):
Andrew Luo8a4f19ed2018-04-19 20:07:47180 """Run CTS tests downloaded from _CTS_BUCKET.
181
182 Downloads CTS tests from bucket, runs them for the
183 specified platform+arch, then creates a single
184 results json file (if specified)
185 Returns 0 if all tests passed, otherwise
186 returns the failure code of the last failing
187 test.
188 """
Andrew Luo96e2fef8d2018-08-22 20:18:24189 local_cts_dir, base_cts_dir, delete_cts_dir = ExtractCTSZip(args)
Andrew Luo8a4f19ed2018-04-19 20:07:47190 cts_result = 0
191 json_results_file = args.json_results_file
mikecase5c5e3f02016-12-14 21:54:21192 try:
Andrew Luo6a84e612018-11-08 04:44:44193 cts_test_runs = GetCtsInfo(args.arch, args.platform, 'test_runs')
Andrew Luo8a4f19ed2018-04-19 20:07:47194 cts_results_json = {}
Andrew Luo6a84e612018-11-08 04:44:44195 for cts_test_run in cts_test_runs:
196 iteration_cts_result = 0
197 if json_results_file:
198 with tempfile.NamedTemporaryFile() as iteration_json_file:
Andrew Luo8a4f19ed2018-04-19 20:07:47199 iteration_cts_result = RunCTS(test_runner_args, local_cts_dir,
Andrew Luo6a84e612018-11-08 04:44:44200 cts_test_run,
201 args.skip_expected_failures,
202 iteration_json_file.name)
203 with open(iteration_json_file.name) as f:
204 additional_results_json = json.load(f)
205 MergeTestResults(cts_results_json, additional_results_json)
206 else:
207 iteration_cts_result = RunCTS(test_runner_args, local_cts_dir,
208 cts_test_run,
209 args.skip_expected_failures)
210 if iteration_cts_result:
211 cts_result = iteration_cts_result
Andrew Luo8a4f19ed2018-04-19 20:07:47212 if json_results_file:
213 with open(json_results_file, 'w') as f:
214 json.dump(cts_results_json, f, indent=2)
mikecase5c5e3f02016-12-14 21:54:21215 finally:
216 if delete_cts_dir and base_cts_dir:
217 shutil.rmtree(base_cts_dir)
Andrew Luo96e2fef8d2018-08-22 20:18:24218
Andrew Luo8a4f19ed2018-04-19 20:07:47219 return cts_result
mikecase5c5e3f02016-12-14 21:54:21220
221
Andrew Luo96e2fef8d2018-08-22 20:18:24222def DeterminePlatform(device):
223 """Determines the platform based on the Android SDK level
224
225 Returns the first letter of the platform in uppercase
226 if platform is found, otherwise returns None
227 """
228 return _SDK_PLATFORM_DICT.get(device.build_version_sdk)
229
230
mikecase5c5e3f02016-12-14 21:54:21231def main():
232 parser = argparse.ArgumentParser()
233 parser.add_argument(
234 '--arch',
Mike Case348008162017-06-27 18:35:29235 choices=['arm64'],
236 default='arm64',
mikecase5c5e3f02016-12-14 21:54:21237 help='Arch for CTS tests.')
238 parser.add_argument(
239 '--platform',
Andrew Luo6924b1b52018-04-20 18:37:11240 choices=['L', 'M', 'N', 'O'],
Andrew Luo96e2fef8d2018-08-22 20:18:24241 required=False,
242 default=None,
243 help='Android platform version for CTS tests. '
244 'Will auto-determine based on SDK level by default.')
mikecase5c5e3f02016-12-14 21:54:21245 parser.add_argument(
246 '--skip-expected-failures',
247 action='store_true',
248 help='Option to skip all tests that are expected to fail.')
249 parser.add_argument(
250 '--apk-dir',
Andrew Luo96e2fef8d2018-08-22 20:18:24251 help='Directory to extract CTS APKs to. '
252 'Will use temp directory by default.')
Andrew Luo8a4f19ed2018-04-19 20:07:47253 parser.add_argument(
254 '--test-launcher-summary-output',
255 '--json-results-file',
Andrew Luo96e2fef8d2018-08-22 20:18:24256 '--write-full-results-to',
Andrew Luobb250612018-10-04 21:46:21257 '--isolated-script-test-output',
Andrew Luo8a4f19ed2018-04-19 20:07:47258 dest='json_results_file', type=os.path.realpath,
259 help='If set, will dump results in JSON form to the specified file. '
260 'Note that this will also trigger saving per-test logcats to '
261 'logdog.')
Andrew Luo96e2fef8d2018-08-22 20:18:24262 script_common.AddDeviceArguments(parser)
mikecase5c5e3f02016-12-14 21:54:21263
264 args, test_runner_args = parser.parse_known_args()
265 devil_chromium.Initialize()
266
Andrew Luo96e2fef8d2018-08-22 20:18:24267 devices = script_common.GetDevices(args.devices, args.blacklist_file)
268 if len(devices) > 1:
269 logging.warning('Only single device supported, using 1st of %d devices: %s',
270 len(devices), devices[0].serial)
271 test_runner_args.extend(['-d', devices[0].serial])
272
273 if args.platform is None:
274 args.platform = DeterminePlatform(devices[0])
275 if args.platform is None:
276 raise Exception('Could not auto-determine device platform, '
277 'please specifiy --platform')
278
279 return RunAllCTSTests(args, test_runner_args)
mikecase5c5e3f02016-12-14 21:54:21280
281
282if __name__ == '__main__':
283 sys.exit(main())