blob: 25df87697a35917aa1020704b71b6b1acecab3be [file] [log] [blame]
Kuang-che Wub9705bd2018-06-28 17:59:18 +08001#!/usr/bin/env python2
2# -*- coding: utf-8 -*-
3# Copyright 2018 The Chromium OS 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"""Evaluate ChromeOS autotest.
7
8Note that by default 'test_that' will install dependency packages of autotest
Kuang-che Wufd2ea5e2019-06-10 20:40:17 +08009if the package checksum mismatch. If you want to override content of autotest
10package, e.g. chrome's test binary, please make sure the autotest version
11matches. Otherwise your test binary will be overwritten.
Kuang-che Wub9705bd2018-06-28 17:59:18 +080012"""
13from __future__ import print_function
14import argparse
Kuang-che Wub9705bd2018-06-28 17:59:18 +080015import logging
16import os
17import re
18import subprocess
19import sys
20
Kuang-che Wu4f0dc232019-05-10 18:28:06 +080021from bisect_kit import catapult_util
Kuang-che Wub9705bd2018-06-28 17:59:18 +080022from bisect_kit import cli
23from bisect_kit import common
24from bisect_kit import configure
25from bisect_kit import cros_util
26from bisect_kit import util
27
28logger = logging.getLogger(__name__)
29
30OLD = 'old'
31NEW = 'new'
32SKIP = 'skip'
33FATAL = 'fatal'
34
35EXIT_CODE_MAP = {
Kuang-che Wu0476d1f2019-03-04 19:27:01 +080036 OLD: cli.EXIT_CODE_OLD,
37 NEW: cli.EXIT_CODE_NEW,
38 SKIP: cli.EXIT_CODE_SKIP,
39 FATAL: cli.EXIT_CODE_FATAL,
Kuang-che Wub9705bd2018-06-28 17:59:18 +080040}
41
42
43def create_argument_parser():
44 parser = argparse.ArgumentParser(description=__doc__)
Kuang-che Wufe1e88a2019-09-10 21:52:25 +080045 cli.patching_argparser_exit(parser)
Kuang-che Wub9705bd2018-06-28 17:59:18 +080046 common.add_common_arguments(parser)
47 parser.add_argument(
48 'dut',
49 nargs='?',
50 type=cli.argtype_notempty,
51 metavar='DUT',
52 default=configure.get('DUT', ''))
53 parser.add_argument(
54 '--chromeos_root',
55 type=cli.argtype_dir_path,
56 metavar='CHROMEOS_ROOT',
57 default=configure.get('CHROMEOS_ROOT', ''),
58 help='ChromeOS tree root')
59 parser.add_argument(
Kuang-che Wud4603d72018-11-29 17:51:21 +080060 '--chrome_root',
61 metavar='CHROME_ROOT',
62 type=cli.argtype_dir_path,
63 default=configure.get('CHROME_ROOT'),
64 help='Chrome tree root; necessary for telemetry tests')
65 parser.add_argument(
Kuang-che Wub9705bd2018-06-28 17:59:18 +080066 '--prebuilt',
67 action='store_true',
68 help='Run autotest using existing prebuilt package if specified; '
69 'otherwise use the default one')
70 parser.add_argument(
71 '--reinstall',
72 action='store_true',
73 help='Remove existing autotest folder on the DUT first')
Kuang-che Wuda3abfe2019-03-21 14:48:12 +080074 parser.add_argument(
75 '--reboot_before_test',
76 action='store_true',
77 help='Reboot before test run')
Kuang-che Wu85c613c2019-01-09 15:46:11 +080078
79 group = parser.add_argument_group(title='Options for normal autotest tests')
80 group.add_argument(
81 '--test_name', help='Test name, like "video_VideoDecodeAccelerator.h264"')
82 group.add_argument(
Kuang-che Wu0a4304a2019-01-19 01:32:11 +080083 '--fail_to_pass',
84 action='store_true',
85 help='For functional tests: old behavior is FAIL and new behavior is '
86 'PASS; If not specified, default = old behavior is PASS and new '
87 'behavior is FAIL')
88 group.add_argument(
Kuang-che Wu85c613c2019-01-09 15:46:11 +080089 '--metric',
90 help=
91 'Metric name of performance test; example: "cheets_SystemRawImageSize"')
92 group.add_argument(
Kuang-che Wub9705bd2018-06-28 17:59:18 +080093 '--args',
94 help='Extra args passed to "test_that --args"; Overrides the default')
Zheng-Jie Chang127c3302019-09-10 17:17:04 +080095 group.add_argument(
96 '--board',
97 metavar='BOARD',
98 default=configure.get('BOARD'),
99 help='ChromeOS board name')
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800100
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800101 group = parser.add_argument_group(title='Options for CTS/GTS tests')
102 group.add_argument('--cts_revision', help='CTS revision, like "9.0_r3"')
Kuang-che Wu63f836a2019-02-21 16:33:32 +0000103 group.add_argument('--cts_abi', choices=['arm', 'x86'])
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800104 group.add_argument(
105 '--cts_prefix',
106 help='Prefix of autotest test name, '
107 'like cheets_CTS_N, cheets_CTS_P, cheets_GTS')
108 group.add_argument(
109 '--cts_module', help='CTS/GTS module name, like "CtsCameraTestCases"')
110 group.add_argument(
111 '--cts_test',
112 help='CTS/GTS test name, like '
113 '"android.hardware.cts.CameraTest#testDisplayOrientation"')
114 group.add_argument('--cts_timeout', type=float, help='timeout, in seconds')
115
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800116 return parser
117
118
119def parse_test_report_log(result_log, metric):
120 """Parses autotest result log.
121
122 Args:
123 result_log: content of test_report.log
124 metric: what metric to capture if not None
125
126 Returns:
127 passed, values:
128 passed: True if test run successfully
129 values: captured metric values; None if test failed or metric is None
130 """
131 m = re.search(r'Total PASS: (\d+)/(\d+)', result_log)
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800132 passed = (m and m.group(1) == m.group(2))
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800133
134 if not metric:
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800135 return passed, None
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800136
137 values = []
138 for line in result_log.splitlines():
139 m = re.match(r'^(\S+)\s+(\w+)(?:\{\d+\})?\s+(\d+\.\d+)$', line)
140 if not m:
141 continue
142 if m.group(2) == metric:
143 values.append(float(m.group(3)))
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800144 return passed, values
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800145
146
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800147def get_additional_test_args(test_name):
148 """Gets extra arguments to specific test.
149
150 Some tests may require special arguments to run.
151
152 Args:
153 test_name: test name
154
155 Returns:
156 arguments (str)
157 """
158 if test_name.startswith('telemetry_'):
159 return 'local=True'
160 return ''
161
162
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800163def prepare_to_run_test(opts):
164 # Some versions of ChromeOS SDK is broken and ship bad 'ssh' executable. This
Kuang-che Wu9fcf1082019-03-04 11:24:04 +0800165 # works around the issue. See crbug/906289 for detail.
166 # TODO(kcwu): remove this workaround once we no longer support bisecting
167 # versions earlier than R73-11445.0.0.
168 ssh_path = os.path.join(opts.chromeos_root, 'chroot/usr/bin/ssh')
Kuang-che Wu4fbd2d32019-03-07 01:07:57 +0800169 if os.path.exists(ssh_path):
170 if 'file descriptor passing not supported' in open(ssh_path).read():
171 cros_util.cros_sdk(opts.chromeos_root, 'sudo', 'emerge',
172 'net-misc/openssh')
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800173
Kuang-che Wuf0ad9ac2019-09-06 13:05:04 +0800174 # test_that may use this ssh key and ssh complains its permission is too open.
Kuang-che Wu4a81ea72019-10-05 15:35:17 +0800175 # chmod every time just before run test_that because the permission may change
Kuang-che Wuf0ad9ac2019-09-06 13:05:04 +0800176 # after some git operations.
177 util.check_call(
178 'chmod',
179 'o-r,g-r',
180 'src/scripts/mod_for_test_scripts/ssh_keys/testing_rsa',
181 cwd=opts.chromeos_root)
182
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800183 if opts.reinstall:
Kuang-che Wu44278142019-03-04 11:33:57 +0800184 util.ssh_cmd(opts.dut, 'rm', '-rf', '/usr/local/autotest')
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800185
Kuang-che Wuda3abfe2019-03-21 14:48:12 +0800186 if opts.reboot_before_test:
187 cros_util.reboot(opts.dut)
188
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800189
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800190def run_test(opts):
191 """Runs an autotest test.
192
193 Args:
194 opts: An argparse.Namespace to hold command line arguments.
195
196 Returns:
197 path of test result (outside chroot)
198 """
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800199 prebuilt_autotest_dir = os.path.join(cros_util.chromeos_root_inside_chroot,
200 cros_util.prebuilt_autotest_dir)
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800201 # Set results dir inside source tree, so it's easier to access them outside
202 # chroot.
203 results_dir = os.path.join(cros_util.chromeos_root_inside_chroot,
204 'tmp/autotest_results_tmp')
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800205 if opts.prebuilt:
206 test_that_bin = os.path.join(prebuilt_autotest_dir,
207 'site_utils/test_that.py')
208 else:
209 test_that_bin = '/usr/bin/test_that'
Kuang-che Wuf3d03ca2019-03-11 17:31:40 +0800210 cmd = [
211 test_that_bin, opts.dut, opts.test_name, '--debug', '--results_dir',
Zheng-Jie Chang127c3302019-09-10 17:17:04 +0800212 results_dir, '--board', opts.board
Kuang-che Wuf3d03ca2019-03-11 17:31:40 +0800213 ]
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800214 if opts.prebuilt:
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800215 cmd += ['--autotest_dir', prebuilt_autotest_dir]
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800216
217 args = get_additional_test_args(opts.test_name)
218 if opts.args:
219 if args:
Kuang-che Wu74768d32018-09-07 12:03:24 +0800220 logger.info(
221 'default test_that args `%s` is overridden by '
222 'command line option `%s`', args, opts.args)
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800223 cmd += ['--args', opts.args]
224 elif args:
225 cmd += ['--args', args]
226
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800227 try:
Kuang-che Wud4603d72018-11-29 17:51:21 +0800228 output = cros_util.cros_sdk(
229 opts.chromeos_root, *cmd, chrome_root=opts.chrome_root)
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800230 except subprocess.CalledProcessError as e:
231 output = e.output
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800232
233 m = re.search(r'Finished running tests. Results can be found in (\S+)',
234 output)
235 if not m:
236 logger.error('result dir is unknown')
237 return None
238 assert m.group(1) == results_dir
239 return results_dir.replace(cros_util.chromeos_root_inside_chroot,
240 opts.chromeos_root)
241
242
243def gather_test_result(opts, result_dir):
244 result_log_path = os.path.join(result_dir, 'test_report.log')
245 result_log = open(result_log_path).read()
246
247 passed, values = parse_test_report_log(result_log, opts.metric)
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800248 if opts.metric and not values:
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800249 values = []
250 for root, _, files in os.walk(result_dir):
251 for filename in files:
252 if filename != 'results-chart.json':
253 continue
254 full_path = os.path.join(root, filename)
Kuang-che Wu4f0dc232019-05-10 18:28:06 +0800255 values = catapult_util.get_benchmark_values(full_path, opts.metric)
256 return passed, values
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800257
258 return passed, values
259
260
Kuang-che Wufe1e88a2019-09-10 21:52:25 +0800261@cli.fatal_error_handler
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800262def main(args=None):
263 common.init()
264 parser = create_argument_parser()
265 opts = parser.parse_args(args)
266 common.config_logging(opts)
267
268 if not cros_util.is_dut(opts.dut):
Kuang-che Wu4a75f952019-03-26 17:22:42 +0800269 logger.error('%r is not a valid DUT address', opts.dut)
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800270 return FATAL
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800271 dut_os_version = cros_util.query_dut_short_version(opts.dut)
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800272
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800273 is_cts = (
274 opts.cts_revision or opts.cts_abi or opts.cts_prefix or opts.cts_module or
Kuang-che Wue07bff52019-10-29 20:50:45 +0800275 opts.cts_test or opts.cts_timeout)
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800276 if is_cts:
277 if opts.test_name or opts.metric or opts.args:
278 parser.error(
279 'do not specify --test_name, --metric, --args for CTS/GTS tests')
Kuang-che Wub91b1512019-05-29 15:16:29 +0800280 if not opts.cts_prefix:
281 parser.error('--cts_prefix should be specified for CTS/GTS tests')
282 if not opts.cts_module:
283 parser.error('--cts_module should be specified for CTS/GTS tests')
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800284 opts.test_name = '%s.tradefed-run-test' % opts.cts_prefix
Kuang-che Wue07bff52019-10-29 20:50:45 +0800285 opts.args = 'module=%s test=%s max_retry=0' % (opts.cts_module,
286 opts.cts_test)
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800287 if opts.cts_revision:
288 opts.args += ' revision=%s' % opts.cts_revision
289 if opts.cts_abi:
290 opts.args += ' abi=%s' % opts.cts_abi
291 if opts.cts_timeout:
292 opts.args += ' timeout=%s' % opts.cts_timeout
293 else:
294 if not opts.test_name:
295 parser.error('argument --test_name is required')
296
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800297 # Verify command line options.
298 if opts.metric:
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800299 if opts.fail_to_pass:
300 logger.error('--fail_to_pass is not for benchmark test (--metric)')
301 return FATAL
Kuang-che Wud4603d72018-11-29 17:51:21 +0800302 if opts.test_name.startswith('telemetry_'):
303 if not opts.chrome_root:
304 logger.error('--chrome_root is mandatory for telemetry tests')
305 return FATAL
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800306
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800307 try:
308 prepare_to_run_test(opts)
309 except Exception:
310 logger.exception('failed when prepare, assume it is temporary; SKIP')
311 return SKIP
Kuang-che Wue47162d2018-10-29 17:24:04 +0800312
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800313 result_dir = run_test(opts)
314 if not result_dir:
Kuang-che Wudd802672018-08-10 19:40:14 +0800315 return FATAL
316
Kuang-che Wuc986b1d2019-04-15 16:45:20 +0800317 try:
318 passed, values = gather_test_result(opts, result_dir)
319 except Exception:
320 logger.exception('failed to parse test result')
321 return FATAL
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800322
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800323 # Sanity check. The OS version should not change.
Kuang-che Wu523bdf22019-08-20 12:11:09 +0800324 assert dut_os_version == cros_util.query_dut_short_version(opts.dut), \
325 'Someone else reflashed the DUT. ' \
326 'DUT locking is not respected? b/126141102'
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800327
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800328 if opts.metric:
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800329 if not values:
330 logger.warning('no values found; SKIP')
331 return SKIP
332
333 print('BISECT_RESULT_VALUES=', ' '.join(map(str, values)))
Kuang-che Wu81cde452019-04-08 16:56:51 +0800334 logger.info('values=%s', values)
335 # The exit code doesn't matter.
336 return OLD
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800337 else:
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800338 if opts.fail_to_pass:
339 if passed:
340 logger.info('passed')
341 return NEW
342 logger.info('failed')
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800343 return OLD
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800344 else:
345 if passed:
346 logger.info('passed')
347 return OLD
348 logger.info('failed')
349 return NEW
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800350
351
352if __name__ == '__main__':
353 sys.exit(EXIT_CODE_MAP[main()])