blob: 5cb548f9b6f4fb60f4f4d82f52e6604a418898d4 [file] [log] [blame]
Kuang-che Wu32f27242019-05-16 17:34:50 +08001#!/usr/bin/env python2
2# -*- coding: utf-8 -*-
3# Copyright 2019 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 tast tests."""
7from __future__ import print_function
8import argparse
9import json
10import logging
11import os
12import re
Kuang-che Wu6c51d302019-06-19 09:07:20 +080013import shutil
Kuang-che Wu32f27242019-05-16 17:34:50 +080014import subprocess
15import sys
16
17from bisect_kit import catapult_util
18from bisect_kit import cli
19from bisect_kit import common
20from bisect_kit import configure
21from bisect_kit import cros_util
Kuang-che Wu11713052019-05-30 16:21:54 +080022from bisect_kit import util
Kuang-che Wu32f27242019-05-16 17:34:50 +080023
24logger = logging.getLogger(__name__)
25
26OLD = 'old'
27NEW = 'new'
28SKIP = 'skip'
29FATAL = 'fatal'
30
31EXIT_CODE_MAP = {
32 OLD: cli.EXIT_CODE_OLD,
33 NEW: cli.EXIT_CODE_NEW,
34 SKIP: cli.EXIT_CODE_SKIP,
35 FATAL: cli.EXIT_CODE_FATAL,
36}
37
38
39def create_argument_parser():
40 parser = argparse.ArgumentParser(description=__doc__)
41 common.add_common_arguments(parser)
42 parser.add_argument(
43 'dut',
44 nargs='?',
45 type=cli.argtype_notempty,
46 metavar='DUT',
47 default=configure.get('DUT', ''))
48 parser.add_argument(
49 '--chromeos_root',
50 type=cli.argtype_dir_path,
51 metavar='CHROMEOS_ROOT',
52 default=configure.get('CHROMEOS_ROOT', ''),
53 help='ChromeOS tree root')
54 parser.add_argument(
55 '--tast_build',
56 action='store_true',
57 help='Build tast test bundle (-build=true) if specified; '
58 'default is using prebuilt bundle on the DUT')
59 parser.add_argument(
60 '--reboot_before_test',
61 action='store_true',
62 help='Reboot before test run')
63
64 group = parser.add_argument_group(title='Options for normal autotest tests')
65 group.add_argument(
66 '--test_name',
67 required=True,
68 help='Test name, like "video_VideoDecodeAccelerator.h264"')
69 group.add_argument(
70 '--fail_to_pass',
71 action='store_true',
72 help='For functional tests: old behavior is FAIL and new behavior is '
73 'PASS; If not specified, default = old behavior is PASS and new '
74 'behavior is FAIL')
75 group.add_argument(
76 '--metric',
77 help=
78 'Metric name of performance test; example: "cheets_SystemRawImageSize"')
79
80 return parser
81
82
83def prepare_to_run_test(opts):
84 if opts.reboot_before_test:
85 cros_util.reboot(opts.dut)
86
87
Kuang-che Wu11713052019-05-30 16:21:54 +080088def list_tast_tests(opts, pattern=None):
89 cmd = ['tast', 'list']
90 if not opts.tast_build:
91 cmd.append('-build=false')
92 cmd.append(opts.dut)
93 if pattern:
94 cmd.append(pattern)
95 return cros_util.cros_sdk(opts.chromeos_root, *cmd).splitlines()
96
97
Kuang-che Wu32f27242019-05-16 17:34:50 +080098def run_test(opts):
99 """Runs an autotest test.
100
101 Args:
102 opts: An argparse.Namespace to hold command line arguments.
103
104 Returns:
105 path of test result (outside chroot)
106 """
107 # Set results dir inside source tree, so it's easier to access them outside
108 # chroot.
109 results_dir = os.path.join(cros_util.chromeos_root_inside_chroot,
110 'tmp/tast_results_tmp')
Kuang-che Wu6c51d302019-06-19 09:07:20 +0800111 results_dir_output_chroot = results_dir.replace(
112 cros_util.chromeos_root_inside_chroot, opts.chromeos_root)
113 # Don't reuse existing results dir, otherwise tast may rename output files.
114 if os.path.exists(results_dir_output_chroot):
115 shutil.rmtree(results_dir_output_chroot)
116
Kuang-che Wu32f27242019-05-16 17:34:50 +0800117 # TODO(kcwu): add -timeout
118 cmd = ['tast', '-verbose', 'run']
119 if not opts.tast_build:
120 cmd.append('-build=false')
121 cmd += ['-resultsdir', results_dir, opts.dut, opts.test_name]
122
123 try:
124 cros_util.cros_sdk(opts.chromeos_root, *cmd)
125 except subprocess.CalledProcessError:
126 logger.fatal('failed to run tast, incorrect test name?')
127 return None
128
Kuang-che Wu6c51d302019-06-19 09:07:20 +0800129 return results_dir_output_chroot
Kuang-che Wu32f27242019-05-16 17:34:50 +0800130
131
132def gather_test_result(opts, result_dir):
133 error_path = os.path.join(result_dir, 'run_error.txt')
134 if os.path.exists(error_path):
135 message = open(error_path).read()
136 raise Exception('tast global error: %s' % message)
137
138 results_path = os.path.join(result_dir, 'results.json')
139 passed = None
140 for result in json.load(open(results_path)):
141 if result['name'] != opts.test_name:
142 logger.warning('unexpected test ran: %s', result['name'])
143 continue
144 passed = result['errors'] is None
145 if passed is None:
146 raise Exception('no test result for "%s"?' % opts.test_name)
147
148 values = []
149 if opts.metric:
150 chart_path = os.path.join(result_dir, 'tests', opts.test_name,
151 'results-chart.json')
152 values = catapult_util.get_benchmark_values(chart_path, opts.metric)
153
154 return passed, values
155
156
157def main(args=None):
158 common.init()
159 parser = create_argument_parser()
160 opts = parser.parse_args(args)
161 common.config_logging(opts)
162
163 if not cros_util.is_dut(opts.dut):
164 logger.error('%r is not a valid DUT address', opts.dut)
165 return FATAL
166
167 # Verify command line options.
168 if opts.metric:
169 if opts.fail_to_pass:
170 logger.error('--fail_to_pass is not for benchmark test (--metric)')
171 return FATAL
172 # Remove the "tast." prefix prepended by autotest.
173 opts.test_name = re.sub(r'^tast\.', '', opts.test_name)
174
Kuang-che Wu11713052019-05-30 16:21:54 +0800175 tests = list_tast_tests(opts, opts.test_name)
176 if tests:
177 if len(tests) != 1 or tests[0] != opts.test_name:
178 # For example, tast in chroot after 12205.0.0 is IPC incompatible with
179 # tast on DUT earlier than 12028.0.0 (crbug/932307)
180 logger.fatal('"tast list" returns unexpected tests; '
181 'incompatible tast on DUT and in chroot?')
182 return FATAL
183 else:
184 tests = list_tast_tests(opts)
185 util.show_similar_candidates('test name', opts.test_name, tests)
186 return FATAL
187
Kuang-che Wu32f27242019-05-16 17:34:50 +0800188 try:
189 prepare_to_run_test(opts)
190 except Exception:
191 logger.exception('failed when prepare, assume it is temporary; SKIP')
192 return SKIP
193
194 result_dir = run_test(opts)
195 if not result_dir:
196 return FATAL
197
198 try:
199 passed, values = gather_test_result(opts, result_dir)
200 except Exception:
201 logger.exception('failed to parse test result')
202 return FATAL
203
204 if opts.metric:
205 if not values:
206 logger.warning('no values found; SKIP')
207 return SKIP
208
209 print('BISECT_RESULT_VALUES=', ' '.join(map(str, values)))
210 logger.info('values=%s', values)
211 # The exit code doesn't matter.
212 return OLD
213 else:
214 if opts.fail_to_pass:
215 if passed:
216 logger.info('passed')
217 return NEW
218 logger.info('failed')
219 return OLD
220 else:
221 if passed:
222 logger.info('passed')
223 return OLD
224 logger.info('failed')
225 return NEW
226
227
228if __name__ == '__main__':
229 sys.exit(EXIT_CODE_MAP[main()])