blob: da9f472dd27114adbd8e26602739343e22619326 [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
9only once. For example, if you overwrote chrome's unittest binary, your new
Kuang-che Wu927231f2018-07-24 14:21:56 +080010binary will be persistent across autotest runs. Add --reinstall if you want
Kuang-che Wub9705bd2018-06-28 17:59:18 +080011clean autotest install.
12"""
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__)
45 common.add_common_arguments(parser)
46 parser.add_argument(
47 'dut',
48 nargs='?',
49 type=cli.argtype_notempty,
50 metavar='DUT',
51 default=configure.get('DUT', ''))
52 parser.add_argument(
53 '--chromeos_root',
54 type=cli.argtype_dir_path,
55 metavar='CHROMEOS_ROOT',
56 default=configure.get('CHROMEOS_ROOT', ''),
57 help='ChromeOS tree root')
58 parser.add_argument(
Kuang-che Wud4603d72018-11-29 17:51:21 +080059 '--chrome_root',
60 metavar='CHROME_ROOT',
61 type=cli.argtype_dir_path,
62 default=configure.get('CHROME_ROOT'),
63 help='Chrome tree root; necessary for telemetry tests')
64 parser.add_argument(
Kuang-che Wub9705bd2018-06-28 17:59:18 +080065 '--prebuilt',
66 action='store_true',
67 help='Run autotest using existing prebuilt package if specified; '
68 'otherwise use the default one')
69 parser.add_argument(
70 '--reinstall',
71 action='store_true',
72 help='Remove existing autotest folder on the DUT first')
Kuang-che Wuda3abfe2019-03-21 14:48:12 +080073 parser.add_argument(
74 '--reboot_before_test',
75 action='store_true',
76 help='Reboot before test run')
Kuang-che Wu85c613c2019-01-09 15:46:11 +080077
78 group = parser.add_argument_group(title='Options for normal autotest tests')
79 group.add_argument(
80 '--test_name', help='Test name, like "video_VideoDecodeAccelerator.h264"')
81 group.add_argument(
Kuang-che Wu0a4304a2019-01-19 01:32:11 +080082 '--fail_to_pass',
83 action='store_true',
84 help='For functional tests: old behavior is FAIL and new behavior is '
85 'PASS; If not specified, default = old behavior is PASS and new '
86 'behavior is FAIL')
87 group.add_argument(
Kuang-che Wu85c613c2019-01-09 15:46:11 +080088 '--metric',
89 help=
90 'Metric name of performance test; example: "cheets_SystemRawImageSize"')
91 group.add_argument(
Kuang-che Wub9705bd2018-06-28 17:59:18 +080092 '--args',
93 help='Extra args passed to "test_that --args"; Overrides the default')
94
Kuang-che Wu85c613c2019-01-09 15:46:11 +080095 group = parser.add_argument_group(title='Options for CTS/GTS tests')
96 group.add_argument('--cts_revision', help='CTS revision, like "9.0_r3"')
Kuang-che Wu63f836a2019-02-21 16:33:32 +000097 group.add_argument('--cts_abi', choices=['arm', 'x86'])
Kuang-che Wu85c613c2019-01-09 15:46:11 +080098 group.add_argument(
99 '--cts_prefix',
100 help='Prefix of autotest test name, '
101 'like cheets_CTS_N, cheets_CTS_P, cheets_GTS')
102 group.add_argument(
103 '--cts_module', help='CTS/GTS module name, like "CtsCameraTestCases"')
104 group.add_argument(
105 '--cts_test',
106 help='CTS/GTS test name, like '
107 '"android.hardware.cts.CameraTest#testDisplayOrientation"')
108 group.add_argument('--cts_timeout', type=float, help='timeout, in seconds')
109
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800110 return parser
111
112
113def parse_test_report_log(result_log, metric):
114 """Parses autotest result log.
115
116 Args:
117 result_log: content of test_report.log
118 metric: what metric to capture if not None
119
120 Returns:
121 passed, values:
122 passed: True if test run successfully
123 values: captured metric values; None if test failed or metric is None
124 """
125 m = re.search(r'Total PASS: (\d+)/(\d+)', result_log)
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800126 passed = (m and m.group(1) == m.group(2))
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800127
128 if not metric:
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800129 return passed, None
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800130
131 values = []
132 for line in result_log.splitlines():
133 m = re.match(r'^(\S+)\s+(\w+)(?:\{\d+\})?\s+(\d+\.\d+)$', line)
134 if not m:
135 continue
136 if m.group(2) == metric:
137 values.append(float(m.group(3)))
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800138 return passed, values
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800139
140
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800141def get_additional_test_args(test_name):
142 """Gets extra arguments to specific test.
143
144 Some tests may require special arguments to run.
145
146 Args:
147 test_name: test name
148
149 Returns:
150 arguments (str)
151 """
152 if test_name.startswith('telemetry_'):
153 return 'local=True'
154 return ''
155
156
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800157def prepare_to_run_test(opts):
158 # Some versions of ChromeOS SDK is broken and ship bad 'ssh' executable. This
Kuang-che Wu9fcf1082019-03-04 11:24:04 +0800159 # works around the issue. See crbug/906289 for detail.
160 # TODO(kcwu): remove this workaround once we no longer support bisecting
161 # versions earlier than R73-11445.0.0.
162 ssh_path = os.path.join(opts.chromeos_root, 'chroot/usr/bin/ssh')
Kuang-che Wu4fbd2d32019-03-07 01:07:57 +0800163 if os.path.exists(ssh_path):
164 if 'file descriptor passing not supported' in open(ssh_path).read():
165 cros_util.cros_sdk(opts.chromeos_root, 'sudo', 'emerge',
166 'net-misc/openssh')
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800167
168 if opts.reinstall:
Kuang-che Wu44278142019-03-04 11:33:57 +0800169 util.ssh_cmd(opts.dut, 'rm', '-rf', '/usr/local/autotest')
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800170
Kuang-che Wuda3abfe2019-03-21 14:48:12 +0800171 if opts.reboot_before_test:
172 cros_util.reboot(opts.dut)
173
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800174
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800175def run_test(opts):
176 """Runs an autotest test.
177
178 Args:
179 opts: An argparse.Namespace to hold command line arguments.
180
181 Returns:
182 path of test result (outside chroot)
183 """
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800184 prebuilt_autotest_dir = os.path.join(cros_util.chromeos_root_inside_chroot,
185 cros_util.prebuilt_autotest_dir)
186 # Set results dir inside source tree, so it's easier to access them outside
187 # chroot.
188 results_dir = os.path.join(cros_util.chromeos_root_inside_chroot,
189 'tmp/autotest_results_tmp')
190 if opts.prebuilt:
191 test_that_bin = os.path.join(prebuilt_autotest_dir,
192 'site_utils/test_that.py')
193 else:
194 test_that_bin = '/usr/bin/test_that'
Kuang-che Wuf3d03ca2019-03-11 17:31:40 +0800195 cmd = [
196 test_that_bin, opts.dut, opts.test_name, '--debug', '--results_dir',
197 results_dir
198 ]
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800199 if opts.prebuilt:
200 cmd += ['--autotest_dir', prebuilt_autotest_dir]
201
202 args = get_additional_test_args(opts.test_name)
203 if opts.args:
204 if args:
Kuang-che Wu74768d32018-09-07 12:03:24 +0800205 logger.info(
206 'default test_that args `%s` is overridden by '
207 'command line option `%s`', args, opts.args)
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800208 cmd += ['--args', opts.args]
209 elif args:
210 cmd += ['--args', args]
211
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800212 try:
Kuang-che Wud4603d72018-11-29 17:51:21 +0800213 output = cros_util.cros_sdk(
214 opts.chromeos_root, *cmd, chrome_root=opts.chrome_root)
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800215 except subprocess.CalledProcessError as e:
216 output = e.output
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800217
218 m = re.search(r'Finished running tests. Results can be found in (\S+)',
219 output)
220 if not m:
221 logger.error('result dir is unknown')
222 return None
223 assert m.group(1) == results_dir
224 return results_dir.replace(cros_util.chromeos_root_inside_chroot,
225 opts.chromeos_root)
226
227
228def gather_test_result(opts, result_dir):
229 result_log_path = os.path.join(result_dir, 'test_report.log')
230 result_log = open(result_log_path).read()
231
232 passed, values = parse_test_report_log(result_log, opts.metric)
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800233 if opts.metric and not values:
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800234 values = []
235 for root, _, files in os.walk(result_dir):
236 for filename in files:
237 if filename != 'results-chart.json':
238 continue
239 full_path = os.path.join(root, filename)
Kuang-che Wu4f0dc232019-05-10 18:28:06 +0800240 values = catapult_util.get_benchmark_values(full_path, opts.metric)
241 return passed, values
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800242
243 return passed, values
244
245
246def main(args=None):
247 common.init()
248 parser = create_argument_parser()
249 opts = parser.parse_args(args)
250 common.config_logging(opts)
251
252 if not cros_util.is_dut(opts.dut):
Kuang-che Wu4a75f952019-03-26 17:22:42 +0800253 logger.error('%r is not a valid DUT address', opts.dut)
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800254 return FATAL
255
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800256 is_cts = (
257 opts.cts_revision or opts.cts_abi or opts.cts_prefix or opts.cts_module or
258 opts.cts_test or opts.cts_timeout)
259 if is_cts:
260 if opts.test_name or opts.metric or opts.args:
261 parser.error(
262 'do not specify --test_name, --metric, --args for CTS/GTS tests')
Kuang-che Wub91b1512019-05-29 15:16:29 +0800263 if not opts.cts_prefix:
264 parser.error('--cts_prefix should be specified for CTS/GTS tests')
265 if not opts.cts_module:
266 parser.error('--cts_module should be specified for CTS/GTS tests')
Kuang-che Wu85c613c2019-01-09 15:46:11 +0800267 opts.test_name = '%s.tradefed-run-test' % opts.cts_prefix
268 opts.args = 'module=%s test=%s' % (opts.cts_module, opts.cts_test)
269 if opts.cts_revision:
270 opts.args += ' revision=%s' % opts.cts_revision
271 if opts.cts_abi:
272 opts.args += ' abi=%s' % opts.cts_abi
273 if opts.cts_timeout:
274 opts.args += ' timeout=%s' % opts.cts_timeout
275 else:
276 if not opts.test_name:
277 parser.error('argument --test_name is required')
278
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800279 # Verify command line options.
280 if opts.metric:
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800281 if opts.fail_to_pass:
282 logger.error('--fail_to_pass is not for benchmark test (--metric)')
283 return FATAL
Kuang-che Wud4603d72018-11-29 17:51:21 +0800284 if opts.test_name.startswith('telemetry_'):
285 if not opts.chrome_root:
286 logger.error('--chrome_root is mandatory for telemetry tests')
287 return FATAL
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800288
Kuang-che Wu6c5a5b22019-01-17 18:09:50 +0800289 try:
290 prepare_to_run_test(opts)
291 except Exception:
292 logger.exception('failed when prepare, assume it is temporary; SKIP')
293 return SKIP
Kuang-che Wue47162d2018-10-29 17:24:04 +0800294
Kuang-che Wu171dcb62018-10-25 12:37:05 +0800295 result_dir = run_test(opts)
296 if not result_dir:
Kuang-che Wudd802672018-08-10 19:40:14 +0800297 return FATAL
298
Kuang-che Wuc986b1d2019-04-15 16:45:20 +0800299 try:
300 passed, values = gather_test_result(opts, result_dir)
301 except Exception:
302 logger.exception('failed to parse test result')
303 return FATAL
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800304
305 if opts.metric:
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800306 if not values:
307 logger.warning('no values found; SKIP')
308 return SKIP
309
310 print('BISECT_RESULT_VALUES=', ' '.join(map(str, values)))
Kuang-che Wu81cde452019-04-08 16:56:51 +0800311 logger.info('values=%s', values)
312 # The exit code doesn't matter.
313 return OLD
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800314 else:
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800315 if opts.fail_to_pass:
316 if passed:
317 logger.info('passed')
318 return NEW
319 logger.info('failed')
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800320 return OLD
Kuang-che Wu0a4304a2019-01-19 01:32:11 +0800321 else:
322 if passed:
323 logger.info('passed')
324 return OLD
325 logger.info('failed')
326 return NEW
Kuang-che Wub9705bd2018-06-28 17:59:18 +0800327
328
329if __name__ == '__main__':
330 sys.exit(EXIT_CODE_MAP[main()])