blob: 787b6d4ad7162e759b3c06a287e24396e42edfbe [file] [log] [blame]
Felix Guo6ebf5862019-09-23 02:02:43 -07001#!/usr/bin/python3
2# SPDX-License-Identifier: GPL-2.0
3#
4# A thin wrapper on top of the KUnit Kernel
5#
6# Copyright (C) 2019, Google LLC.
7# Author: Felix Guo <felixguoxiuping@gmail.com>
8# Author: Brendan Higgins <brendanhiggins@google.com>
9
10import argparse
11import sys
12import os
13import time
Brendan Higginsff7b4372019-09-23 02:02:44 -070014import shutil
Felix Guo6ebf5862019-09-23 02:02:43 -070015
16from collections import namedtuple
17from enum import Enum, auto
18
19import kunit_config
20import kunit_kernel
21import kunit_parser
22
David Gow45ba7a82020-04-30 21:27:01 -070023KunitResult = namedtuple('KunitResult', ['status','result','elapsed_time'])
Felix Guo6ebf5862019-09-23 02:02:43 -070024
David Gow45ba7a82020-04-30 21:27:01 -070025KunitConfigRequest = namedtuple('KunitConfigRequest',
Vitor Massaru Iha01397e82020-05-29 16:28:45 -030026 ['build_dir', 'make_options'])
David Gow45ba7a82020-04-30 21:27:01 -070027KunitBuildRequest = namedtuple('KunitBuildRequest',
28 ['jobs', 'build_dir', 'alltests',
29 'make_options'])
30KunitExecRequest = namedtuple('KunitExecRequest',
31 ['timeout', 'build_dir', 'alltests'])
32KunitParseRequest = namedtuple('KunitParseRequest',
33 ['raw_output', 'input_data'])
Heidi Fahim021ed9f2020-03-16 13:21:25 -070034KunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs',
Vitor Massaru Iha9bdf64b2020-04-14 20:37:53 -030035 'build_dir', 'alltests',
36 'make_options'])
Felix Guo6ebf5862019-09-23 02:02:43 -070037
Heidi Fahimbe886ba2020-02-18 14:19:16 -080038KernelDirectoryPath = sys.argv[0].split('tools/testing/kunit/')[0]
39
Felix Guo6ebf5862019-09-23 02:02:43 -070040class KunitStatus(Enum):
41 SUCCESS = auto()
42 CONFIG_FAILURE = auto()
43 BUILD_FAILURE = auto()
44 TEST_FAILURE = auto()
45
Brendan Higginsff7b4372019-09-23 02:02:44 -070046def create_default_kunitconfig():
SeongJae Parke3212512019-12-20 05:14:05 +000047 if not os.path.exists(kunit_kernel.kunitconfig_path):
Brendan Higginsff7b4372019-09-23 02:02:44 -070048 shutil.copyfile('arch/um/configs/kunit_defconfig',
SeongJae Parke3212512019-12-20 05:14:05 +000049 kunit_kernel.kunitconfig_path)
Brendan Higginsff7b4372019-09-23 02:02:44 -070050
Heidi Fahimbe886ba2020-02-18 14:19:16 -080051def get_kernel_root_path():
52 parts = sys.argv[0] if not __file__ else __file__
53 parts = os.path.realpath(parts).split('tools/testing/kunit')
54 if len(parts) != 2:
55 sys.exit(1)
56 return parts[0]
57
David Gow45ba7a82020-04-30 21:27:01 -070058def config_tests(linux: kunit_kernel.LinuxSourceTree,
59 request: KunitConfigRequest) -> KunitResult:
60 kunit_parser.print_with_timestamp('Configuring KUnit Kernel ...')
61
Felix Guo6ebf5862019-09-23 02:02:43 -070062 config_start = time.time()
Vitor Massaru Iha9bdf64b2020-04-14 20:37:53 -030063 create_default_kunitconfig()
Greg Thelen0476e692020-03-23 12:04:59 -070064 success = linux.build_reconfig(request.build_dir, request.make_options)
Felix Guo6ebf5862019-09-23 02:02:43 -070065 config_end = time.time()
66 if not success:
David Gow45ba7a82020-04-30 21:27:01 -070067 return KunitResult(KunitStatus.CONFIG_FAILURE,
68 'could not configure kernel',
69 config_end - config_start)
70 return KunitResult(KunitStatus.SUCCESS,
71 'configured kernel successfully',
72 config_end - config_start)
Felix Guo6ebf5862019-09-23 02:02:43 -070073
David Gow45ba7a82020-04-30 21:27:01 -070074def build_tests(linux: kunit_kernel.LinuxSourceTree,
75 request: KunitBuildRequest) -> KunitResult:
Felix Guo6ebf5862019-09-23 02:02:43 -070076 kunit_parser.print_with_timestamp('Building KUnit Kernel ...')
77
78 build_start = time.time()
Heidi Fahim021ed9f2020-03-16 13:21:25 -070079 success = linux.build_um_kernel(request.alltests,
80 request.jobs,
Greg Thelen0476e692020-03-23 12:04:59 -070081 request.build_dir,
82 request.make_options)
Felix Guo6ebf5862019-09-23 02:02:43 -070083 build_end = time.time()
84 if not success:
85 return KunitResult(KunitStatus.BUILD_FAILURE, 'could not build kernel')
David Gow45ba7a82020-04-30 21:27:01 -070086 if not success:
87 return KunitResult(KunitStatus.BUILD_FAILURE,
88 'could not build kernel',
89 build_end - build_start)
90 return KunitResult(KunitStatus.SUCCESS,
91 'built kernel successfully',
92 build_end - build_start)
Felix Guo6ebf5862019-09-23 02:02:43 -070093
David Gow45ba7a82020-04-30 21:27:01 -070094def exec_tests(linux: kunit_kernel.LinuxSourceTree,
95 request: KunitExecRequest) -> KunitResult:
Felix Guo6ebf5862019-09-23 02:02:43 -070096 kunit_parser.print_with_timestamp('Starting KUnit Kernel ...')
97 test_start = time.time()
David Gow45ba7a82020-04-30 21:27:01 -070098 result = linux.run_kernel(
Heidi Fahim021ed9f2020-03-16 13:21:25 -070099 timeout=None if request.alltests else request.timeout,
100 build_dir=request.build_dir)
David Gow45ba7a82020-04-30 21:27:01 -0700101
Felix Guo6ebf5862019-09-23 02:02:43 -0700102 test_end = time.time()
103
David Gow45ba7a82020-04-30 21:27:01 -0700104 return KunitResult(KunitStatus.SUCCESS,
105 result,
106 test_end - test_start)
107
108def parse_tests(request: KunitParseRequest) -> KunitResult:
109 parse_start = time.time()
110
111 test_result = kunit_parser.TestResult(kunit_parser.TestStatus.SUCCESS,
112 [],
113 'Tests not Parsed.')
114 if request.raw_output:
115 kunit_parser.raw_output(request.input_data)
116 else:
117 test_result = kunit_parser.parse_run_tests(request.input_data)
118 parse_end = time.time()
119
120 if test_result.status != kunit_parser.TestStatus.SUCCESS:
121 return KunitResult(KunitStatus.TEST_FAILURE, test_result,
122 parse_end - parse_start)
123
124 return KunitResult(KunitStatus.SUCCESS, test_result,
125 parse_end - parse_start)
126
127
128def run_tests(linux: kunit_kernel.LinuxSourceTree,
129 request: KunitRequest) -> KunitResult:
130 run_start = time.time()
131
132 config_request = KunitConfigRequest(request.build_dir,
David Gow45ba7a82020-04-30 21:27:01 -0700133 request.make_options)
134 config_result = config_tests(linux, config_request)
135 if config_result.status != KunitStatus.SUCCESS:
136 return config_result
137
138 build_request = KunitBuildRequest(request.jobs, request.build_dir,
139 request.alltests,
140 request.make_options)
141 build_result = build_tests(linux, build_request)
142 if build_result.status != KunitStatus.SUCCESS:
143 return build_result
144
145 exec_request = KunitExecRequest(request.timeout, request.build_dir,
146 request.alltests)
147 exec_result = exec_tests(linux, exec_request)
148 if exec_result.status != KunitStatus.SUCCESS:
149 return exec_result
150
151 parse_request = KunitParseRequest(request.raw_output,
152 exec_result.result)
153 parse_result = parse_tests(parse_request)
154
155 run_end = time.time()
156
Felix Guo6ebf5862019-09-23 02:02:43 -0700157 kunit_parser.print_with_timestamp((
158 'Elapsed time: %.3fs total, %.3fs configuring, %.3fs ' +
159 'building, %.3fs running\n') % (
David Gow45ba7a82020-04-30 21:27:01 -0700160 run_end - run_start,
161 config_result.elapsed_time,
162 build_result.elapsed_time,
163 exec_result.elapsed_time))
164 return parse_result
Felix Guo6ebf5862019-09-23 02:02:43 -0700165
David Gow45ba7a82020-04-30 21:27:01 -0700166def add_common_opts(parser):
167 parser.add_argument('--build_dir',
168 help='As in the make command, it specifies the build '
169 'directory.',
Vitor Massaru Ihaddbd60c2020-04-14 20:09:50 -0300170 type=str, default='.kunit', metavar='build_dir')
David Gow45ba7a82020-04-30 21:27:01 -0700171 parser.add_argument('--make_options',
172 help='X=Y make option, can be repeated.',
173 action='append')
174 parser.add_argument('--alltests',
175 help='Run all KUnit tests through allyesconfig',
176 action='store_true')
177
David Gow45ba7a82020-04-30 21:27:01 -0700178def add_build_opts(parser):
179 parser.add_argument('--jobs',
180 help='As in the make command, "Specifies the number of '
181 'jobs (commands) to run simultaneously."',
182 type=int, default=8, metavar='jobs')
183
184def add_exec_opts(parser):
185 parser.add_argument('--timeout',
186 help='maximum number of seconds to allow for all tests '
187 'to run. This does not include time taken to build the '
188 'tests.',
189 type=int,
190 default=300,
191 metavar='timeout')
192
193def add_parse_opts(parser):
194 parser.add_argument('--raw_output', help='don\'t format output from kernel',
195 action='store_true')
196
Felix Guo6ebf5862019-09-23 02:02:43 -0700197
Brendan Higginsff7b4372019-09-23 02:02:44 -0700198def main(argv, linux=None):
Felix Guo6ebf5862019-09-23 02:02:43 -0700199 parser = argparse.ArgumentParser(
200 description='Helps writing and running KUnit tests.')
201 subparser = parser.add_subparsers(dest='subcommand')
202
David Gow45ba7a82020-04-30 21:27:01 -0700203 # The 'run' command will config, build, exec, and parse in one go.
Felix Guo6ebf5862019-09-23 02:02:43 -0700204 run_parser = subparser.add_parser('run', help='Runs KUnit tests.')
David Gow45ba7a82020-04-30 21:27:01 -0700205 add_common_opts(run_parser)
David Gow45ba7a82020-04-30 21:27:01 -0700206 add_build_opts(run_parser)
207 add_exec_opts(run_parser)
208 add_parse_opts(run_parser)
Felix Guo6ebf5862019-09-23 02:02:43 -0700209
David Gow45ba7a82020-04-30 21:27:01 -0700210 config_parser = subparser.add_parser('config',
211 help='Ensures that .config contains all of '
212 'the options in .kunitconfig')
213 add_common_opts(config_parser)
Felix Guo6ebf5862019-09-23 02:02:43 -0700214
David Gow45ba7a82020-04-30 21:27:01 -0700215 build_parser = subparser.add_parser('build', help='Builds a kernel with KUnit tests')
216 add_common_opts(build_parser)
217 add_build_opts(build_parser)
Felix Guo6ebf5862019-09-23 02:02:43 -0700218
David Gow45ba7a82020-04-30 21:27:01 -0700219 exec_parser = subparser.add_parser('exec', help='Run a kernel with KUnit tests')
220 add_common_opts(exec_parser)
221 add_exec_opts(exec_parser)
222 add_parse_opts(exec_parser)
Felix Guo6ebf5862019-09-23 02:02:43 -0700223
David Gow45ba7a82020-04-30 21:27:01 -0700224 # The 'parse' option is special, as it doesn't need the kernel source
225 # (therefore there is no need for a build_dir, hence no add_common_opts)
226 # and the '--file' argument is not relevant to 'run', so isn't in
227 # add_parse_opts()
228 parse_parser = subparser.add_parser('parse',
229 help='Parses KUnit results from a file, '
230 'and parses formatted results.')
231 add_parse_opts(parse_parser)
232 parse_parser.add_argument('file',
233 help='Specifies the file to read results from.',
234 type=str, nargs='?', metavar='input_file')
Greg Thelen0476e692020-03-23 12:04:59 -0700235
Felix Guo6ebf5862019-09-23 02:02:43 -0700236 cli_args = parser.parse_args(argv)
237
238 if cli_args.subcommand == 'run':
Vitor Massaru Iha01397e82020-05-29 16:28:45 -0300239 if not os.path.exists(cli_args.build_dir):
240 os.mkdir(cli_args.build_dir)
241 kunit_kernel.kunitconfig_path = os.path.join(
242 cli_args.build_dir,
243 kunit_kernel.kunitconfig_path)
244
245 if not os.path.exists(kunit_kernel.kunitconfig_path):
246 create_default_kunitconfig()
SeongJae Parke3212512019-12-20 05:14:05 +0000247
Brendan Higginsff7b4372019-09-23 02:02:44 -0700248 if not linux:
249 linux = kunit_kernel.LinuxSourceTree()
250
Felix Guo6ebf5862019-09-23 02:02:43 -0700251 request = KunitRequest(cli_args.raw_output,
252 cli_args.timeout,
253 cli_args.jobs,
Brendan Higginsff7b4372019-09-23 02:02:44 -0700254 cli_args.build_dir,
Greg Thelen0476e692020-03-23 12:04:59 -0700255 cli_args.alltests,
256 cli_args.make_options)
Felix Guo6ebf5862019-09-23 02:02:43 -0700257 result = run_tests(linux, request)
258 if result.status != KunitStatus.SUCCESS:
259 sys.exit(1)
David Gow45ba7a82020-04-30 21:27:01 -0700260 elif cli_args.subcommand == 'config':
261 if cli_args.build_dir:
262 if not os.path.exists(cli_args.build_dir):
263 os.mkdir(cli_args.build_dir)
264 kunit_kernel.kunitconfig_path = os.path.join(
265 cli_args.build_dir,
266 kunit_kernel.kunitconfig_path)
267
Vitor Massaru Iha01397e82020-05-29 16:28:45 -0300268 if not os.path.exists(kunit_kernel.kunitconfig_path):
269 create_default_kunitconfig()
270
David Gow45ba7a82020-04-30 21:27:01 -0700271 if not linux:
272 linux = kunit_kernel.LinuxSourceTree()
273
274 request = KunitConfigRequest(cli_args.build_dir,
David Gow45ba7a82020-04-30 21:27:01 -0700275 cli_args.make_options)
276 result = config_tests(linux, request)
277 kunit_parser.print_with_timestamp((
278 'Elapsed time: %.3fs\n') % (
279 result.elapsed_time))
280 if result.status != KunitStatus.SUCCESS:
281 sys.exit(1)
282 elif cli_args.subcommand == 'build':
283 if cli_args.build_dir:
284 if not os.path.exists(cli_args.build_dir):
285 os.mkdir(cli_args.build_dir)
286 kunit_kernel.kunitconfig_path = os.path.join(
287 cli_args.build_dir,
288 kunit_kernel.kunitconfig_path)
289
Vitor Massaru Iha01397e82020-05-29 16:28:45 -0300290 if not os.path.exists(kunit_kernel.kunitconfig_path):
291 create_default_kunitconfig()
292
David Gow45ba7a82020-04-30 21:27:01 -0700293 if not linux:
294 linux = kunit_kernel.LinuxSourceTree()
295
296 request = KunitBuildRequest(cli_args.jobs,
297 cli_args.build_dir,
298 cli_args.alltests,
299 cli_args.make_options)
300 result = build_tests(linux, request)
301 kunit_parser.print_with_timestamp((
302 'Elapsed time: %.3fs\n') % (
303 result.elapsed_time))
304 if result.status != KunitStatus.SUCCESS:
305 sys.exit(1)
306 elif cli_args.subcommand == 'exec':
307 if cli_args.build_dir:
308 if not os.path.exists(cli_args.build_dir):
309 os.mkdir(cli_args.build_dir)
310 kunit_kernel.kunitconfig_path = os.path.join(
311 cli_args.build_dir,
312 kunit_kernel.kunitconfig_path)
313
Vitor Massaru Iha01397e82020-05-29 16:28:45 -0300314 if not os.path.exists(kunit_kernel.kunitconfig_path):
315 create_default_kunitconfig()
316
David Gow45ba7a82020-04-30 21:27:01 -0700317 if not linux:
318 linux = kunit_kernel.LinuxSourceTree()
319
320 exec_request = KunitExecRequest(cli_args.timeout,
321 cli_args.build_dir,
322 cli_args.alltests)
323 exec_result = exec_tests(linux, exec_request)
324 parse_request = KunitParseRequest(cli_args.raw_output,
325 exec_result.result)
326 result = parse_tests(parse_request)
327 kunit_parser.print_with_timestamp((
328 'Elapsed time: %.3fs\n') % (
329 exec_result.elapsed_time))
330 if result.status != KunitStatus.SUCCESS:
331 sys.exit(1)
332 elif cli_args.subcommand == 'parse':
333 if cli_args.file == None:
334 kunit_output = sys.stdin
335 else:
336 with open(cli_args.file, 'r') as f:
337 kunit_output = f.read().splitlines()
338 request = KunitParseRequest(cli_args.raw_output,
339 kunit_output)
340 result = parse_tests(request)
341 if result.status != KunitStatus.SUCCESS:
342 sys.exit(1)
Felix Guo6ebf5862019-09-23 02:02:43 -0700343 else:
344 parser.print_help()
345
346if __name__ == '__main__':
Brendan Higginsff7b4372019-09-23 02:02:44 -0700347 main(sys.argv[1:])