blob: 06735a43080afba6b7724622df05944a2e6f0ca1 [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
13import subprocess
14import sys
15
16from bisect_kit import catapult_util
17from bisect_kit import cli
18from bisect_kit import common
19from bisect_kit import configure
20from bisect_kit import cros_util
21
22logger = logging.getLogger(__name__)
23
24OLD = 'old'
25NEW = 'new'
26SKIP = 'skip'
27FATAL = 'fatal'
28
29EXIT_CODE_MAP = {
30 OLD: cli.EXIT_CODE_OLD,
31 NEW: cli.EXIT_CODE_NEW,
32 SKIP: cli.EXIT_CODE_SKIP,
33 FATAL: cli.EXIT_CODE_FATAL,
34}
35
36
37def create_argument_parser():
38 parser = argparse.ArgumentParser(description=__doc__)
39 common.add_common_arguments(parser)
40 parser.add_argument(
41 'dut',
42 nargs='?',
43 type=cli.argtype_notempty,
44 metavar='DUT',
45 default=configure.get('DUT', ''))
46 parser.add_argument(
47 '--chromeos_root',
48 type=cli.argtype_dir_path,
49 metavar='CHROMEOS_ROOT',
50 default=configure.get('CHROMEOS_ROOT', ''),
51 help='ChromeOS tree root')
52 parser.add_argument(
53 '--tast_build',
54 action='store_true',
55 help='Build tast test bundle (-build=true) if specified; '
56 'default is using prebuilt bundle on the DUT')
57 parser.add_argument(
58 '--reboot_before_test',
59 action='store_true',
60 help='Reboot before test run')
61
62 group = parser.add_argument_group(title='Options for normal autotest tests')
63 group.add_argument(
64 '--test_name',
65 required=True,
66 help='Test name, like "video_VideoDecodeAccelerator.h264"')
67 group.add_argument(
68 '--fail_to_pass',
69 action='store_true',
70 help='For functional tests: old behavior is FAIL and new behavior is '
71 'PASS; If not specified, default = old behavior is PASS and new '
72 'behavior is FAIL')
73 group.add_argument(
74 '--metric',
75 help=
76 'Metric name of performance test; example: "cheets_SystemRawImageSize"')
77
78 return parser
79
80
81def prepare_to_run_test(opts):
82 if opts.reboot_before_test:
83 cros_util.reboot(opts.dut)
84
85
86def run_test(opts):
87 """Runs an autotest test.
88
89 Args:
90 opts: An argparse.Namespace to hold command line arguments.
91
92 Returns:
93 path of test result (outside chroot)
94 """
95 # Set results dir inside source tree, so it's easier to access them outside
96 # chroot.
97 results_dir = os.path.join(cros_util.chromeos_root_inside_chroot,
98 'tmp/tast_results_tmp')
99 # TODO(kcwu): add -timeout
100 cmd = ['tast', '-verbose', 'run']
101 if not opts.tast_build:
102 cmd.append('-build=false')
103 cmd += ['-resultsdir', results_dir, opts.dut, opts.test_name]
104
105 try:
106 cros_util.cros_sdk(opts.chromeos_root, *cmd)
107 except subprocess.CalledProcessError:
108 logger.fatal('failed to run tast, incorrect test name?')
109 return None
110
111 return results_dir.replace(cros_util.chromeos_root_inside_chroot,
112 opts.chromeos_root)
113
114
115def gather_test_result(opts, result_dir):
116 error_path = os.path.join(result_dir, 'run_error.txt')
117 if os.path.exists(error_path):
118 message = open(error_path).read()
119 raise Exception('tast global error: %s' % message)
120
121 results_path = os.path.join(result_dir, 'results.json')
122 passed = None
123 for result in json.load(open(results_path)):
124 if result['name'] != opts.test_name:
125 logger.warning('unexpected test ran: %s', result['name'])
126 continue
127 passed = result['errors'] is None
128 if passed is None:
129 raise Exception('no test result for "%s"?' % opts.test_name)
130
131 values = []
132 if opts.metric:
133 chart_path = os.path.join(result_dir, 'tests', opts.test_name,
134 'results-chart.json')
135 values = catapult_util.get_benchmark_values(chart_path, opts.metric)
136
137 return passed, values
138
139
140def main(args=None):
141 common.init()
142 parser = create_argument_parser()
143 opts = parser.parse_args(args)
144 common.config_logging(opts)
145
146 if not cros_util.is_dut(opts.dut):
147 logger.error('%r is not a valid DUT address', opts.dut)
148 return FATAL
149
150 # Verify command line options.
151 if opts.metric:
152 if opts.fail_to_pass:
153 logger.error('--fail_to_pass is not for benchmark test (--metric)')
154 return FATAL
155 # Remove the "tast." prefix prepended by autotest.
156 opts.test_name = re.sub(r'^tast\.', '', opts.test_name)
157
158 try:
159 prepare_to_run_test(opts)
160 except Exception:
161 logger.exception('failed when prepare, assume it is temporary; SKIP')
162 return SKIP
163
164 result_dir = run_test(opts)
165 if not result_dir:
166 return FATAL
167
168 try:
169 passed, values = gather_test_result(opts, result_dir)
170 except Exception:
171 logger.exception('failed to parse test result')
172 return FATAL
173
174 if opts.metric:
175 if not values:
176 logger.warning('no values found; SKIP')
177 return SKIP
178
179 print('BISECT_RESULT_VALUES=', ' '.join(map(str, values)))
180 logger.info('values=%s', values)
181 # The exit code doesn't matter.
182 return OLD
183 else:
184 if opts.fail_to_pass:
185 if passed:
186 logger.info('passed')
187 return NEW
188 logger.info('failed')
189 return OLD
190 else:
191 if passed:
192 logger.info('passed')
193 return OLD
194 logger.info('failed')
195 return NEW
196
197
198if __name__ == '__main__':
199 sys.exit(EXIT_CODE_MAP[main()])