blob: f3a1803bd71f3346060224dcb4cb678245059954 [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',
26 ['build_dir', 'defconfig', 'make_options'])
27KunitBuildRequest = 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',
35 'build_dir', 'defconfig',
Greg Thelen0476e692020-03-23 12:04:59 -070036 'alltests', '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()
David Gow45ba7a82020-04-30 21:27:01 -070063 if request.defconfig:
64 create_default_kunitconfig()
Greg Thelen0476e692020-03-23 12:04:59 -070065 success = linux.build_reconfig(request.build_dir, request.make_options)
Felix Guo6ebf5862019-09-23 02:02:43 -070066 config_end = time.time()
67 if not success:
David Gow45ba7a82020-04-30 21:27:01 -070068 return KunitResult(KunitStatus.CONFIG_FAILURE,
69 'could not configure kernel',
70 config_end - config_start)
71 return KunitResult(KunitStatus.SUCCESS,
72 'configured kernel successfully',
73 config_end - config_start)
Felix Guo6ebf5862019-09-23 02:02:43 -070074
David Gow45ba7a82020-04-30 21:27:01 -070075def build_tests(linux: kunit_kernel.LinuxSourceTree,
76 request: KunitBuildRequest) -> KunitResult:
Felix Guo6ebf5862019-09-23 02:02:43 -070077 kunit_parser.print_with_timestamp('Building KUnit Kernel ...')
78
79 build_start = time.time()
Heidi Fahim021ed9f2020-03-16 13:21:25 -070080 success = linux.build_um_kernel(request.alltests,
81 request.jobs,
Greg Thelen0476e692020-03-23 12:04:59 -070082 request.build_dir,
83 request.make_options)
Felix Guo6ebf5862019-09-23 02:02:43 -070084 build_end = time.time()
85 if not success:
86 return KunitResult(KunitStatus.BUILD_FAILURE, 'could not build kernel')
David Gow45ba7a82020-04-30 21:27:01 -070087 if not success:
88 return KunitResult(KunitStatus.BUILD_FAILURE,
89 'could not build kernel',
90 build_end - build_start)
91 return KunitResult(KunitStatus.SUCCESS,
92 'built kernel successfully',
93 build_end - build_start)
Felix Guo6ebf5862019-09-23 02:02:43 -070094
David Gow45ba7a82020-04-30 21:27:01 -070095def exec_tests(linux: kunit_kernel.LinuxSourceTree,
96 request: KunitExecRequest) -> KunitResult:
Felix Guo6ebf5862019-09-23 02:02:43 -070097 kunit_parser.print_with_timestamp('Starting KUnit Kernel ...')
98 test_start = time.time()
David Gow45ba7a82020-04-30 21:27:01 -070099 result = linux.run_kernel(
Heidi Fahim021ed9f2020-03-16 13:21:25 -0700100 timeout=None if request.alltests else request.timeout,
101 build_dir=request.build_dir)
David Gow45ba7a82020-04-30 21:27:01 -0700102
Felix Guo6ebf5862019-09-23 02:02:43 -0700103 test_end = time.time()
104
David Gow45ba7a82020-04-30 21:27:01 -0700105 return KunitResult(KunitStatus.SUCCESS,
106 result,
107 test_end - test_start)
108
109def parse_tests(request: KunitParseRequest) -> KunitResult:
110 parse_start = time.time()
111
112 test_result = kunit_parser.TestResult(kunit_parser.TestStatus.SUCCESS,
113 [],
114 'Tests not Parsed.')
115 if request.raw_output:
116 kunit_parser.raw_output(request.input_data)
117 else:
118 test_result = kunit_parser.parse_run_tests(request.input_data)
119 parse_end = time.time()
120
121 if test_result.status != kunit_parser.TestStatus.SUCCESS:
122 return KunitResult(KunitStatus.TEST_FAILURE, test_result,
123 parse_end - parse_start)
124
125 return KunitResult(KunitStatus.SUCCESS, test_result,
126 parse_end - parse_start)
127
128
129def run_tests(linux: kunit_kernel.LinuxSourceTree,
130 request: KunitRequest) -> KunitResult:
131 run_start = time.time()
132
133 config_request = KunitConfigRequest(request.build_dir,
134 request.defconfig,
135 request.make_options)
136 config_result = config_tests(linux, config_request)
137 if config_result.status != KunitStatus.SUCCESS:
138 return config_result
139
140 build_request = KunitBuildRequest(request.jobs, request.build_dir,
141 request.alltests,
142 request.make_options)
143 build_result = build_tests(linux, build_request)
144 if build_result.status != KunitStatus.SUCCESS:
145 return build_result
146
147 exec_request = KunitExecRequest(request.timeout, request.build_dir,
148 request.alltests)
149 exec_result = exec_tests(linux, exec_request)
150 if exec_result.status != KunitStatus.SUCCESS:
151 return exec_result
152
153 parse_request = KunitParseRequest(request.raw_output,
154 exec_result.result)
155 parse_result = parse_tests(parse_request)
156
157 run_end = time.time()
158
Felix Guo6ebf5862019-09-23 02:02:43 -0700159 kunit_parser.print_with_timestamp((
160 'Elapsed time: %.3fs total, %.3fs configuring, %.3fs ' +
161 'building, %.3fs running\n') % (
David Gow45ba7a82020-04-30 21:27:01 -0700162 run_end - run_start,
163 config_result.elapsed_time,
164 build_result.elapsed_time,
165 exec_result.elapsed_time))
166 return parse_result
Felix Guo6ebf5862019-09-23 02:02:43 -0700167
David Gow45ba7a82020-04-30 21:27:01 -0700168def add_common_opts(parser):
169 parser.add_argument('--build_dir',
170 help='As in the make command, it specifies the build '
171 'directory.',
Vitor Massaru Ihaddbd60c2020-04-14 20:09:50 -0300172 type=str, default='.kunit', metavar='build_dir')
David Gow45ba7a82020-04-30 21:27:01 -0700173 parser.add_argument('--make_options',
174 help='X=Y make option, can be repeated.',
175 action='append')
176 parser.add_argument('--alltests',
177 help='Run all KUnit tests through allyesconfig',
178 action='store_true')
179
180def add_config_opts(parser):
181 parser.add_argument('--defconfig',
182 help='Uses a default .kunitconfig.',
183 action='store_true')
184
185def add_build_opts(parser):
186 parser.add_argument('--jobs',
187 help='As in the make command, "Specifies the number of '
188 'jobs (commands) to run simultaneously."',
189 type=int, default=8, metavar='jobs')
190
191def add_exec_opts(parser):
192 parser.add_argument('--timeout',
193 help='maximum number of seconds to allow for all tests '
194 'to run. This does not include time taken to build the '
195 'tests.',
196 type=int,
197 default=300,
198 metavar='timeout')
199
200def add_parse_opts(parser):
201 parser.add_argument('--raw_output', help='don\'t format output from kernel',
202 action='store_true')
203
Felix Guo6ebf5862019-09-23 02:02:43 -0700204
Brendan Higginsff7b4372019-09-23 02:02:44 -0700205def main(argv, linux=None):
Felix Guo6ebf5862019-09-23 02:02:43 -0700206 parser = argparse.ArgumentParser(
207 description='Helps writing and running KUnit tests.')
208 subparser = parser.add_subparsers(dest='subcommand')
209
David Gow45ba7a82020-04-30 21:27:01 -0700210 # The 'run' command will config, build, exec, and parse in one go.
Felix Guo6ebf5862019-09-23 02:02:43 -0700211 run_parser = subparser.add_parser('run', help='Runs KUnit tests.')
David Gow45ba7a82020-04-30 21:27:01 -0700212 add_common_opts(run_parser)
213 add_config_opts(run_parser)
214 add_build_opts(run_parser)
215 add_exec_opts(run_parser)
216 add_parse_opts(run_parser)
Felix Guo6ebf5862019-09-23 02:02:43 -0700217
David Gow45ba7a82020-04-30 21:27:01 -0700218 config_parser = subparser.add_parser('config',
219 help='Ensures that .config contains all of '
220 'the options in .kunitconfig')
221 add_common_opts(config_parser)
222 add_config_opts(config_parser)
Felix Guo6ebf5862019-09-23 02:02:43 -0700223
David Gow45ba7a82020-04-30 21:27:01 -0700224 build_parser = subparser.add_parser('build', help='Builds a kernel with KUnit tests')
225 add_common_opts(build_parser)
226 add_build_opts(build_parser)
Felix Guo6ebf5862019-09-23 02:02:43 -0700227
David Gow45ba7a82020-04-30 21:27:01 -0700228 exec_parser = subparser.add_parser('exec', help='Run a kernel with KUnit tests')
229 add_common_opts(exec_parser)
230 add_exec_opts(exec_parser)
231 add_parse_opts(exec_parser)
Felix Guo6ebf5862019-09-23 02:02:43 -0700232
David Gow45ba7a82020-04-30 21:27:01 -0700233 # The 'parse' option is special, as it doesn't need the kernel source
234 # (therefore there is no need for a build_dir, hence no add_common_opts)
235 # and the '--file' argument is not relevant to 'run', so isn't in
236 # add_parse_opts()
237 parse_parser = subparser.add_parser('parse',
238 help='Parses KUnit results from a file, '
239 'and parses formatted results.')
240 add_parse_opts(parse_parser)
241 parse_parser.add_argument('file',
242 help='Specifies the file to read results from.',
243 type=str, nargs='?', metavar='input_file')
Greg Thelen0476e692020-03-23 12:04:59 -0700244
Felix Guo6ebf5862019-09-23 02:02:43 -0700245 cli_args = parser.parse_args(argv)
246
247 if cli_args.subcommand == 'run':
Vitor Massaru Ihaddbd60c2020-04-14 20:09:50 -0300248 if not os.path.exists(cli_args.build_dir):
249 os.mkdir(cli_args.build_dir)
250 kunit_kernel.kunitconfig_path = os.path.join(
251 cli_args.build_dir,
252 kunit_kernel.kunitconfig_path)
SeongJae Parke3212512019-12-20 05:14:05 +0000253
Brendan Higginsff7b4372019-09-23 02:02:44 -0700254 if not linux:
255 linux = kunit_kernel.LinuxSourceTree()
256
Felix Guo6ebf5862019-09-23 02:02:43 -0700257 request = KunitRequest(cli_args.raw_output,
258 cli_args.timeout,
259 cli_args.jobs,
Brendan Higginsff7b4372019-09-23 02:02:44 -0700260 cli_args.build_dir,
Heidi Fahim021ed9f2020-03-16 13:21:25 -0700261 cli_args.defconfig,
Greg Thelen0476e692020-03-23 12:04:59 -0700262 cli_args.alltests,
263 cli_args.make_options)
Felix Guo6ebf5862019-09-23 02:02:43 -0700264 result = run_tests(linux, request)
265 if result.status != KunitStatus.SUCCESS:
266 sys.exit(1)
David Gow45ba7a82020-04-30 21:27:01 -0700267 elif cli_args.subcommand == 'config':
268 if cli_args.build_dir:
269 if not os.path.exists(cli_args.build_dir):
270 os.mkdir(cli_args.build_dir)
271 kunit_kernel.kunitconfig_path = os.path.join(
272 cli_args.build_dir,
273 kunit_kernel.kunitconfig_path)
274
275 if not linux:
276 linux = kunit_kernel.LinuxSourceTree()
277
278 request = KunitConfigRequest(cli_args.build_dir,
279 cli_args.defconfig,
280 cli_args.make_options)
281 result = config_tests(linux, request)
282 kunit_parser.print_with_timestamp((
283 'Elapsed time: %.3fs\n') % (
284 result.elapsed_time))
285 if result.status != KunitStatus.SUCCESS:
286 sys.exit(1)
287 elif cli_args.subcommand == 'build':
288 if cli_args.build_dir:
289 if not os.path.exists(cli_args.build_dir):
290 os.mkdir(cli_args.build_dir)
291 kunit_kernel.kunitconfig_path = os.path.join(
292 cli_args.build_dir,
293 kunit_kernel.kunitconfig_path)
294
295 if not linux:
296 linux = kunit_kernel.LinuxSourceTree()
297
298 request = KunitBuildRequest(cli_args.jobs,
299 cli_args.build_dir,
300 cli_args.alltests,
301 cli_args.make_options)
302 result = build_tests(linux, request)
303 kunit_parser.print_with_timestamp((
304 'Elapsed time: %.3fs\n') % (
305 result.elapsed_time))
306 if result.status != KunitStatus.SUCCESS:
307 sys.exit(1)
308 elif cli_args.subcommand == 'exec':
309 if cli_args.build_dir:
310 if not os.path.exists(cli_args.build_dir):
311 os.mkdir(cli_args.build_dir)
312 kunit_kernel.kunitconfig_path = os.path.join(
313 cli_args.build_dir,
314 kunit_kernel.kunitconfig_path)
315
316 if not linux:
317 linux = kunit_kernel.LinuxSourceTree()
318
319 exec_request = KunitExecRequest(cli_args.timeout,
320 cli_args.build_dir,
321 cli_args.alltests)
322 exec_result = exec_tests(linux, exec_request)
323 parse_request = KunitParseRequest(cli_args.raw_output,
324 exec_result.result)
325 result = parse_tests(parse_request)
326 kunit_parser.print_with_timestamp((
327 'Elapsed time: %.3fs\n') % (
328 exec_result.elapsed_time))
329 if result.status != KunitStatus.SUCCESS:
330 sys.exit(1)
331 elif cli_args.subcommand == 'parse':
332 if cli_args.file == None:
333 kunit_output = sys.stdin
334 else:
335 with open(cli_args.file, 'r') as f:
336 kunit_output = f.read().splitlines()
337 request = KunitParseRequest(cli_args.raw_output,
338 kunit_output)
339 result = parse_tests(request)
340 if result.status != KunitStatus.SUCCESS:
341 sys.exit(1)
Felix Guo6ebf5862019-09-23 02:02:43 -0700342 else:
343 parser.print_help()
344
345if __name__ == '__main__':
Brendan Higginsff7b4372019-09-23 02:02:44 -0700346 main(sys.argv[1:])