blob: 8de93713a80e8ad7abf8834bc9a3733f14dbbc47 [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')
Kuang-che Wu958d7d02019-05-30 17:58:21 +0800115 group.add_argument(
116 '--cts_runonce', action='store_true', help='run test only once')
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800117
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800118 return parser
119
120
121def parse_test_report_log(result_log, metric):
122 """Parses autotest result log.
123
124 Args:
125 result_log: content of test_report.log
126 metric: what metric to capture if not None
127
128 Returns:
129 passed, values:
130 passed: True if test run successfully
131 values: captured metric values; None if test failed or metric is None
132 """
133 m = re.search(r'Total PASS: (\d+)/(\d+)', result_log)
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800134 passed = (m and m.group(1) == m.group(2))
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800135
136 if not metric:
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800137 return passed, None
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800138
139 values = []
140 for line in result_log.splitlines():
141 m = re.match(r'^(\S+)\s+(\w+)(?:\{\d+\})?\s+(\d+\.\d+)$', line)
142 if not m:
143 continue
144 if m.group(2) == metric:
145 values.append(float(m.group(3)))
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800146 return passed, values
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800147
148
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800149def get_additional_test_args(test_name):
150 """Gets extra arguments to specific test.
151
152 Some tests may require special arguments to run.
153
154 Args:
155 test_name: test name
156
157 Returns:
158 arguments (str)
159 """
160 if test_name.startswith('telemetry_'):
161 return 'local=True'
162 return ''
163
164
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800165def prepare_to_run_test(opts):
166 # Some versions of ChromeOS SDK is broken and ship bad 'ssh' executable. This
Kuang-che Wu9fcf1082019-03-04 11:24:04 +0800167 # works around the issue. See crbug/906289 for detail.
168 # TODO(kcwu): remove this workaround once we no longer support bisecting
169 # versions earlier than R73-11445.0.0.
170 ssh_path = os.path.join(opts.chromeos_root, 'chroot/usr/bin/ssh')
Kuang-che Wu4fbd2d32019-03-07 01:07:57 +0800171 if os.path.exists(ssh_path):
172 if 'file descriptor passing not supported' in open(ssh_path).read():
173 cros_util.cros_sdk(opts.chromeos_root, 'sudo', 'emerge',
174 'net-misc/openssh')
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800175
Kuang-che Wuf0ad9ac2019-09-06 13:05:04 +0800176 # test_that may use this ssh key and ssh complains its permission is too open.
Kuang-che Wu4a81ea72019-10-05 15:35:17 +0800177 # chmod every time just before run test_that because the permission may change
Kuang-che Wuf0ad9ac2019-09-06 13:05:04 +0800178 # after some git operations.
179 util.check_call(
180 'chmod',
181 'o-r,g-r',
182 'src/scripts/mod_for_test_scripts/ssh_keys/testing_rsa',
183 cwd=opts.chromeos_root)
184
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800185 if opts.reinstall:
Kuang-che Wu44278142019-03-04 11:33:57 +0800186 util.ssh_cmd(opts.dut, 'rm', '-rf', '/usr/local/autotest')
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800187
Kuang-che Wuda3abfe2019-03-21 14:48:12 +0800188 if opts.reboot_before_test:
189 cros_util.reboot(opts.dut)
190
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800191
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800192def run_test(opts):
193 """Runs an autotest test.
194
195 Args:
196 opts: An argparse.Namespace to hold command line arguments.
197
198 Returns:
199 path of test result (outside chroot)
200 """
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800201 prebuilt_autotest_dir = os.path.join(cros_util.chromeos_root_inside_chroot,
202 cros_util.prebuilt_autotest_dir)
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800203 # Set results dir inside source tree, so it's easier to access them outside
204 # chroot.
205 results_dir = os.path.join(cros_util.chromeos_root_inside_chroot,
206 'tmp/autotest_results_tmp')
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800207 if opts.prebuilt:
208 test_that_bin = os.path.join(prebuilt_autotest_dir,
209 'site_utils/test_that.py')
210 else:
211 test_that_bin = '/usr/bin/test_that'
Kuang-che Wuf3d03ca2019-03-11 17:31:40 +0800212 cmd = [
213 test_that_bin, opts.dut, opts.test_name, '--debug', '--results_dir',
Zheng-Jie Chang127c3302019-09-10 17:17:04 +0800214 results_dir, '--board', opts.board
Kuang-che Wuf3d03ca2019-03-11 17:31:40 +0800215 ]
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800216 if opts.prebuilt:
Kuang-che Wu7f82c6f2019-08-12 14:29:28 +0800217 cmd += ['--autotest_dir', prebuilt_autotest_dir]
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800218
219 args = get_additional_test_args(opts.test_name)
220 if opts.args:
221 if args:
Kuang-che Wu74768d32018-09-07 12:03:24 +0800222 logger.info(
223 'default test_that args `%s` is overridden by '
224 'command line option `%s`', args, opts.args)
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800225 cmd += ['--args', opts.args]
226 elif args:
227 cmd += ['--args', args]
228
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800229 try:
Kuang-che Wud4603d72018-11-29 17:51:21 +0800230 output = cros_util.cros_sdk(
231 opts.chromeos_root, *cmd, chrome_root=opts.chrome_root)
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800232 except subprocess.CalledProcessError as e:
233 output = e.output
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800234
235 m = re.search(r'Finished running tests. Results can be found in (\S+)',
236 output)
237 if not m:
238 logger.error('result dir is unknown')
239 return None
240 assert m.group(1) == results_dir
241 return results_dir.replace(cros_util.chromeos_root_inside_chroot,
242 opts.chromeos_root)
243
244
245def gather_test_result(opts, result_dir):
246 result_log_path = os.path.join(result_dir, 'test_report.log')
247 result_log = open(result_log_path).read()
248
249 passed, values = parse_test_report_log(result_log, opts.metric)
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800250 if opts.metric and not values:
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800251 values = []
252 for root, _, files in os.walk(result_dir):
253 for filename in files:
254 if filename != 'results-chart.json':
255 continue
256 full_path = os.path.join(root, filename)
Kuang-che Wu4f0dc232019-05-10 18:28:06 +0800257 values = catapult_util.get_benchmark_values(full_path, opts.metric)
258 return passed, values
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800259
260 return passed, values
261
262
Kuang-che Wufe1e88a2019-09-10 21:52:25 +0800263@cli.fatal_error_handler
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800264def main(args=None):
265 common.init()
266 parser = create_argument_parser()
267 opts = parser.parse_args(args)
268 common.config_logging(opts)
269
270 if not cros_util.is_dut(opts.dut):
Kuang-che Wu4a75f952019-03-26 17:22:42 +0800271 logger.error('%r is not a valid DUT address', opts.dut)
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800272 return FATAL
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800273 dut_os_version = cros_util.query_dut_short_version(opts.dut)
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800274
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800275 is_cts = (
276 opts.cts_revision or opts.cts_abi or opts.cts_prefix or opts.cts_module or
Kuang-che Wu958d7d02019-05-30 17:58:21 +0800277 opts.cts_test or opts.cts_runonce or opts.cts_timeout)
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800278 if is_cts:
279 if opts.test_name or opts.metric or opts.args:
280 parser.error(
281 'do not specify --test_name, --metric, --args for CTS/GTS tests')
Kuang-che Wub91b1512019-05-29 15:16:29 +0800282 if not opts.cts_prefix:
283 parser.error('--cts_prefix should be specified for CTS/GTS tests')
284 if not opts.cts_module:
285 parser.error('--cts_module should be specified for CTS/GTS tests')
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800286 opts.test_name = '%s.tradefed-run-test' % opts.cts_prefix
287 opts.args = 'module=%s test=%s' % (opts.cts_module, opts.cts_test)
288 if opts.cts_revision:
289 opts.args += ' revision=%s' % opts.cts_revision
290 if opts.cts_abi:
291 opts.args += ' abi=%s' % opts.cts_abi
Kuang-che Wu958d7d02019-05-30 17:58:21 +0800292 if opts.cts_runonce:
293 opts.args += ' max_retry=0'
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800294 if opts.cts_timeout:
295 opts.args += ' timeout=%s' % opts.cts_timeout
296 else:
297 if not opts.test_name:
298 parser.error('argument --test_name is required')
299
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800300 # Verify command line options.
301 if opts.metric:
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800302 if opts.fail_to_pass:
303 logger.error('--fail_to_pass is not for benchmark test (--metric)')
304 return FATAL
Kuang-che Wud4603d72018-11-29 17:51:21 +0800305 if opts.test_name.startswith('telemetry_'):
306 if not opts.chrome_root:
307 logger.error('--chrome_root is mandatory for telemetry tests')
308 return FATAL
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800309
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800310 try:
311 prepare_to_run_test(opts)
312 except Exception:
313 logger.exception('failed when prepare, assume it is temporary; SKIP')
314 return SKIP
Kuang-che Wue47162d2018-10-29 17:24:04 +0800315
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800316 result_dir = run_test(opts)
317 if not result_dir:
Kuang-che Wudd802672018-08-10 19:40:14 +0800318 return FATAL
319
Kuang-che Wuc986b1d2019-04-15 16:45:20 +0800320 try:
321 passed, values = gather_test_result(opts, result_dir)
322 except Exception:
323 logger.exception('failed to parse test result')
324 return FATAL
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800325
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800326 # Sanity check. The OS version should not change.
Kuang-che Wu523bdf22019-08-20 12:11:09 +0800327 assert dut_os_version == cros_util.query_dut_short_version(opts.dut), \
328 'Someone else reflashed the DUT. ' \
329 'DUT locking is not respected? b/126141102'
Kuang-che Wuc8c495d2019-08-19 17:48:58 +0800330
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800331 if opts.metric:
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800332 if not values:
333 logger.warning('no values found; SKIP')
334 return SKIP
335
336 print('BISECT_RESULT_VALUES=', ' '.join(map(str, values)))
Kuang-che Wu81cde452019-04-08 16:56:51 +0800337 logger.info('values=%s', values)
338 # The exit code doesn't matter.
339 return OLD
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800340 else:
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800341 if opts.fail_to_pass:
342 if passed:
343 logger.info('passed')
344 return NEW
345 logger.info('failed')
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800346 return OLD
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800347 else:
348 if passed:
349 logger.info('passed')
350 return OLD
351 logger.info('failed')
352 return NEW
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800353
354
355if __name__ == '__main__':
356 sys.exit(EXIT_CODE_MAP[main()])