1 #!/usr/bin/env python3 1 #!/usr/bin/env python3 2 # SPDX-License-Identifier: GPL-2.0 2 # SPDX-License-Identifier: GPL-2.0 3 # 3 # 4 # A thin wrapper on top of the KUnit Kernel 4 # A thin wrapper on top of the KUnit Kernel 5 # 5 # 6 # Copyright (C) 2019, Google LLC. 6 # Copyright (C) 2019, Google LLC. 7 # Author: Felix Guo <felixguoxiuping@gmail.com> 7 # Author: Felix Guo <felixguoxiuping@gmail.com> 8 # Author: Brendan Higgins <brendanhiggins@googl 8 # Author: Brendan Higgins <brendanhiggins@google.com> 9 9 10 import argparse 10 import argparse 11 import os << 12 import re << 13 import shlex << 14 import sys 11 import sys >> 12 import os 15 import time 13 import time 16 14 17 assert sys.version_info >= (3, 7), "Python ver 15 assert sys.version_info >= (3, 7), "Python version is too old" 18 16 19 from dataclasses import dataclass !! 17 from collections import namedtuple 20 from enum import Enum, auto 18 from enum import Enum, auto 21 from typing import Iterable, List, Optional, S << 22 19 >> 20 import kunit_config 23 import kunit_json 21 import kunit_json 24 import kunit_kernel 22 import kunit_kernel 25 import kunit_parser 23 import kunit_parser 26 from kunit_printer import stdout !! 24 >> 25 KunitResult = namedtuple('KunitResult', ['status','result','elapsed_time']) >> 26 >> 27 KunitConfigRequest = namedtuple('KunitConfigRequest', >> 28 ['build_dir', 'make_options']) >> 29 KunitBuildRequest = namedtuple('KunitBuildRequest', >> 30 ['jobs', 'build_dir', 'alltests', >> 31 'make_options']) >> 32 KunitExecRequest = namedtuple('KunitExecRequest', >> 33 ['timeout', 'build_dir', 'alltests', 'filter_glob']) >> 34 KunitParseRequest = namedtuple('KunitParseRequest', >> 35 ['raw_output', 'input_data', 'build_dir', 'json']) >> 36 KunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs', >> 37 'build_dir', 'alltests', 'filter_glob', >> 38 'json', 'make_options']) >> 39 >> 40 KernelDirectoryPath = sys.argv[0].split('tools/testing/kunit/')[0] 27 41 28 class KunitStatus(Enum): 42 class KunitStatus(Enum): 29 SUCCESS = auto() 43 SUCCESS = auto() 30 CONFIG_FAILURE = auto() 44 CONFIG_FAILURE = auto() 31 BUILD_FAILURE = auto() 45 BUILD_FAILURE = auto() 32 TEST_FAILURE = auto() 46 TEST_FAILURE = auto() 33 47 34 @dataclass << 35 class KunitResult: << 36 status: KunitStatus << 37 elapsed_time: float << 38 << 39 @dataclass << 40 class KunitConfigRequest: << 41 build_dir: str << 42 make_options: Optional[List[str]] << 43 << 44 @dataclass << 45 class KunitBuildRequest(KunitConfigRequest): << 46 jobs: int << 47 << 48 @dataclass << 49 class KunitParseRequest: << 50 raw_output: Optional[str] << 51 json: Optional[str] << 52 << 53 @dataclass << 54 class KunitExecRequest(KunitParseRequest): << 55 build_dir: str << 56 timeout: int << 57 filter_glob: str << 58 filter: str << 59 filter_action: Optional[str] << 60 kernel_args: Optional[List[str]] << 61 run_isolated: Optional[str] << 62 list_tests: bool << 63 list_tests_attr: bool << 64 << 65 @dataclass << 66 class KunitRequest(KunitExecRequest, KunitBuil << 67 pass << 68 << 69 << 70 def get_kernel_root_path() -> str: 48 def get_kernel_root_path() -> str: 71 path = sys.argv[0] if not __file__ els 49 path = sys.argv[0] if not __file__ else __file__ 72 parts = os.path.realpath(path).split(' 50 parts = os.path.realpath(path).split('tools/testing/kunit') 73 if len(parts) != 2: 51 if len(parts) != 2: 74 sys.exit(1) 52 sys.exit(1) 75 return parts[0] 53 return parts[0] 76 54 77 def config_tests(linux: kunit_kernel.LinuxSour 55 def config_tests(linux: kunit_kernel.LinuxSourceTree, 78 request: KunitConfigRequest) 56 request: KunitConfigRequest) -> KunitResult: 79 stdout.print_with_timestamp('Configuri !! 57 kunit_parser.print_with_timestamp('Configuring KUnit Kernel ...') 80 58 81 config_start = time.time() 59 config_start = time.time() 82 success = linux.build_reconfig(request 60 success = linux.build_reconfig(request.build_dir, request.make_options) 83 config_end = time.time() 61 config_end = time.time() 84 status = KunitStatus.SUCCESS if succes !! 62 if not success: 85 return KunitResult(status, config_end !! 63 return KunitResult(KunitStatus.CONFIG_FAILURE, >> 64 'could not configure kernel', >> 65 config_end - config_start) >> 66 return KunitResult(KunitStatus.SUCCESS, >> 67 'configured kernel successfully', >> 68 config_end - config_start) 86 69 87 def build_tests(linux: kunit_kernel.LinuxSourc 70 def build_tests(linux: kunit_kernel.LinuxSourceTree, 88 request: KunitBuildRequest) -> 71 request: KunitBuildRequest) -> KunitResult: 89 stdout.print_with_timestamp('Building !! 72 kunit_parser.print_with_timestamp('Building KUnit Kernel ...') 90 73 91 build_start = time.time() 74 build_start = time.time() 92 success = linux.build_kernel(request.j !! 75 success = linux.build_kernel(request.alltests, >> 76 request.jobs, 93 request.b 77 request.build_dir, 94 request.m 78 request.make_options) 95 build_end = time.time() 79 build_end = time.time() 96 status = KunitStatus.SUCCESS if succes !! 80 if not success: 97 return KunitResult(status, build_end - !! 81 return KunitResult(KunitStatus.BUILD_FAILURE, 98 !! 82 'could not build kernel', 99 def config_and_build_tests(linux: kunit_kernel !! 83 build_end - build_start) 100 request: KunitBuild !! 84 if not success: 101 config_result = config_tests(linux, re !! 85 return KunitResult(KunitStatus.BUILD_FAILURE, 102 if config_result.status != KunitStatus !! 86 'could not build kernel', 103 return config_result !! 87 build_end - build_start) 104 !! 88 return KunitResult(KunitStatus.SUCCESS, 105 return build_tests(linux, request) !! 89 'built kernel successfully', 106 !! 90 build_end - build_start) 107 def _list_tests(linux: kunit_kernel.LinuxSourc !! 91 108 args = ['kunit.action=list'] !! 92 def exec_tests(linux: kunit_kernel.LinuxSourceTree, 109 !! 93 request: KunitExecRequest) -> KunitResult: 110 if request.kernel_args: !! 94 kunit_parser.print_with_timestamp('Starting KUnit Kernel ...') 111 args.extend(request.kernel_arg !! 95 test_start = time.time() 112 !! 96 result = linux.run_kernel( 113 output = linux.run_kernel(args=args, !! 97 timeout=None if request.alltests else request.timeout, 114 timeout=request.tim !! 98 filter_glob=request.filter_glob, 115 filter_glob=request !! 99 build_dir=request.build_dir) 116 filter=request.filt !! 100 117 filter_action=reque !! 101 test_end = time.time() 118 build_dir=request.b !! 102 119 lines = kunit_parser.extract_tap_lines !! 103 return KunitResult(KunitStatus.SUCCESS, 120 # Hack! Drop the dummy TAP version hea !! 104 result, 121 lines.pop() !! 105 test_end - test_start) 122 << 123 # Filter out any extraneous non-test o << 124 return [l for l in output if re.match( << 125 << 126 def _list_tests_attr(linux: kunit_kernel.Linux << 127 args = ['kunit.action=list_attr'] << 128 << 129 if request.kernel_args: << 130 args.extend(request.kernel_arg << 131 << 132 output = linux.run_kernel(args=args, << 133 timeout=request.tim << 134 filter_glob=request << 135 filter=request.filt << 136 filter_action=reque << 137 build_dir=request.b << 138 lines = kunit_parser.extract_tap_lines << 139 # Hack! Drop the dummy TAP version hea << 140 lines.pop() << 141 << 142 # Filter out any extraneous non-test o << 143 return lines << 144 << 145 def _suites_from_test_list(tests: List[str]) - << 146 """Extracts all the suites from an ord << 147 suites = [] # type: List[str] << 148 for t in tests: << 149 parts = t.split('.', maxsplit= << 150 if len(parts) != 2: << 151 raise ValueError(f'int << 152 suite, _ = parts << 153 if not suites or suites[-1] != << 154 suites.append(suite) << 155 return suites << 156 << 157 def exec_tests(linux: kunit_kernel.LinuxSource << 158 filter_globs = [request.filter_glob] << 159 if request.list_tests: << 160 output = _list_tests(linux, re << 161 for line in output: << 162 print(line.rstrip()) << 163 return KunitResult(status=Kuni << 164 if request.list_tests_attr: << 165 attr_output = _list_tests_attr << 166 for line in attr_output: << 167 print(line.rstrip()) << 168 return KunitResult(status=Kuni << 169 if request.run_isolated: << 170 tests = _list_tests(linux, req << 171 if request.run_isolated == 'te << 172 filter_globs = tests << 173 elif request.run_isolated == ' << 174 filter_globs = _suites << 175 # Apply the test-part << 176 if '.' in request.filt << 177 test_glob = re << 178 filter_globs = << 179 << 180 metadata = kunit_json.Metadata(arch=li << 181 << 182 test_counts = kunit_parser.TestCounts( << 183 exec_time = 0.0 << 184 for i, filter_glob in enumerate(filter << 185 stdout.print_with_timestamp('S << 186 << 187 test_start = time.time() << 188 run_result = linux.run_kernel( << 189 args=request.kernel_ar << 190 timeout=request.timeou << 191 filter_glob=filter_glo << 192 filter=request.filter, << 193 filter_action=request. << 194 build_dir=request.buil << 195 << 196 _, test_result = parse_tests(r << 197 # run_kernel() doesn't block o << 198 # That only happens after we g << 199 # So exec_time here actually c << 200 test_end = time.time() << 201 exec_time += test_end - test_s << 202 << 203 test_counts.add_subtest_counts << 204 << 205 if len(filter_globs) == 1 and test_cou << 206 bd = request.build_dir << 207 print('The kernel seems to hav << 208 print('$ scripts/decode_stackt << 209 bd, bd, kunit_ << 210 << 211 kunit_status = _map_to_overall_status( << 212 return KunitResult(status=kunit_status << 213 << 214 def _map_to_overall_status(test_status: kunit_ << 215 if test_status in (kunit_parser.TestSt << 216 return KunitStatus.SUCCESS << 217 return KunitStatus.TEST_FAILURE << 218 106 219 def parse_tests(request: KunitParseRequest, me !! 107 def parse_tests(request: KunitParseRequest) -> KunitResult: 220 parse_start = time.time() 108 parse_start = time.time() 221 109 >> 110 test_result = kunit_parser.TestResult(kunit_parser.TestStatus.SUCCESS, >> 111 [], >> 112 'Tests not Parsed.') >> 113 222 if request.raw_output: 114 if request.raw_output: 223 # Treat unparsed results as on !! 115 kunit_parser.raw_output(request.input_data) 224 fake_test = kunit_parser.Test( !! 116 else: 225 fake_test.status = kunit_parse !! 117 test_result = kunit_parser.parse_run_tests(request.input_data) 226 fake_test.counts.passed = 1 !! 118 parse_end = time.time() 227 << 228 output: Iterable[str] = input_ << 229 if request.raw_output == 'all' << 230 pass << 231 elif request.raw_output == 'ku << 232 output = kunit_parser. << 233 for line in output: << 234 print(line.rstrip()) << 235 parse_time = time.time() - par << 236 return KunitResult(KunitStatus << 237 << 238 << 239 # Actually parse the test results. << 240 test = kunit_parser.parse_run_tests(in << 241 parse_time = time.time() - parse_start << 242 119 243 if request.json: 120 if request.json: 244 json_str = kunit_json.get_json !! 121 json_obj = kunit_json.get_json_result( 245 test=t !! 122 test_result=test_result, 246 metada !! 123 def_config='kunit_defconfig', >> 124 build_dir=request.build_dir, >> 125 json_path=request.json) 247 if request.json == 'stdout': 126 if request.json == 'stdout': 248 print(json_str) !! 127 print(json_obj) 249 else: !! 128 250 with open(request.json !! 129 if test_result.status != kunit_parser.TestStatus.SUCCESS: 251 f.write(json_s !! 130 return KunitResult(KunitStatus.TEST_FAILURE, test_result, 252 stdout.print_with_time !! 131 parse_end - parse_start) 253 os.path.abspat << 254 132 255 if test.status != kunit_parser.TestSta !! 133 return KunitResult(KunitStatus.SUCCESS, test_result, 256 return KunitResult(KunitStatus !! 134 parse_end - parse_start) 257 135 258 return KunitResult(KunitStatus.SUCCESS << 259 136 260 def run_tests(linux: kunit_kernel.LinuxSourceT 137 def run_tests(linux: kunit_kernel.LinuxSourceTree, 261 request: KunitRequest) -> KunitR 138 request: KunitRequest) -> KunitResult: 262 run_start = time.time() 139 run_start = time.time() 263 140 264 config_result = config_tests(linux, re !! 141 config_request = KunitConfigRequest(request.build_dir, >> 142 request.make_options) >> 143 config_result = config_tests(linux, config_request) 265 if config_result.status != KunitStatus 144 if config_result.status != KunitStatus.SUCCESS: 266 return config_result 145 return config_result 267 146 268 build_result = build_tests(linux, requ !! 147 build_request = KunitBuildRequest(request.jobs, request.build_dir, >> 148 request.alltests, >> 149 request.make_options) >> 150 build_result = build_tests(linux, build_request) 269 if build_result.status != KunitStatus. 151 if build_result.status != KunitStatus.SUCCESS: 270 return build_result 152 return build_result 271 153 272 exec_result = exec_tests(linux, reques !! 154 exec_request = KunitExecRequest(request.timeout, request.build_dir, >> 155 request.alltests, request.filter_glob) >> 156 exec_result = exec_tests(linux, exec_request) >> 157 if exec_result.status != KunitStatus.SUCCESS: >> 158 return exec_result >> 159 >> 160 parse_request = KunitParseRequest(request.raw_output, >> 161 exec_result.result, >> 162 request.build_dir, >> 163 request.json) >> 164 parse_result = parse_tests(parse_request) 273 165 274 run_end = time.time() 166 run_end = time.time() 275 167 276 stdout.print_with_timestamp(( !! 168 kunit_parser.print_with_timestamp(( 277 'Elapsed time: %.3fs total, %. 169 'Elapsed time: %.3fs total, %.3fs configuring, %.3fs ' + 278 'building, %.3fs running\n') % 170 'building, %.3fs running\n') % ( 279 run_end - run_ 171 run_end - run_start, 280 config_result. 172 config_result.elapsed_time, 281 build_result.e 173 build_result.elapsed_time, 282 exec_result.el 174 exec_result.elapsed_time)) 283 return exec_result !! 175 return parse_result 284 176 285 # Problem: !! 177 def add_common_opts(parser) -> None: 286 # $ kunit.py run --json << 287 # works as one would expect and prints the par << 288 # $ kunit.py run --json suite_name << 289 # would *not* pass suite_name as the filter_gl << 290 # argparse will consider it to be another way << 291 # $ kunit.py run --json=suite_name << 292 # i.e. it would run all tests, and dump the js << 293 # So we hackily automatically rewrite --json = << 294 pseudo_bool_flag_defaults = { << 295 '--json': 'stdout', << 296 '--raw_output': 'kunit', << 297 } << 298 def massage_argv(argv: Sequence[str]) -> Seque << 299 def massage_arg(arg: str) -> str: << 300 if arg not in pseudo_bool_flag << 301 return arg << 302 return f'{arg}={pseudo_bool_f << 303 return list(map(massage_arg, argv)) << 304 << 305 def get_default_jobs() -> int: << 306 return len(os.sched_getaffinity(0)) << 307 << 308 def add_common_opts(parser: argparse.ArgumentP << 309 parser.add_argument('--build_dir', 178 parser.add_argument('--build_dir', 310 help='As in the ma 179 help='As in the make command, it specifies the build ' 311 'directory.', 180 'directory.', 312 type=str, default= !! 181 type=str, default='.kunit', metavar='build_dir') 313 parser.add_argument('--make_options', 182 parser.add_argument('--make_options', 314 help='X=Y make opt 183 help='X=Y make option, can be repeated.', 315 action='append', m !! 184 action='append') 316 parser.add_argument('--alltests', 185 parser.add_argument('--alltests', 317 help='Run all KUni !! 186 help='Run all KUnit tests through allyesconfig', 318 action='store_true 187 action='store_true') 319 parser.add_argument('--kunitconfig', 188 parser.add_argument('--kunitconfig', 320 help='Path to Kco 189 help='Path to Kconfig fragment that enables KUnit tests.' 321 ' If given a dire 190 ' If given a directory, (e.g. lib/kunit), "/.kunitconfig" ' 322 'will get automa !! 191 'will get automatically appended.', 323 'blindly concaten !! 192 metavar='kunitconfig') 324 action='append', << 325 parser.add_argument('--kconfig_add', << 326 help='Additional << 327 '.kunitconfig, e. << 328 action='append', m << 329 193 330 parser.add_argument('--arch', 194 parser.add_argument('--arch', 331 help=('Specifies t 195 help=('Specifies the architecture to run tests under. ' 332 'The archite 196 'The architecture specified here must match the ' 333 'string pass 197 'string passed to the ARCH make param, ' 334 'e.g. i386, 198 'e.g. i386, x86_64, arm, um, etc. Non-UML ' 335 'architectur 199 'architectures run on QEMU.'), 336 type=str, default= !! 200 type=str, default='um', metavar='arch') 337 201 338 parser.add_argument('--cross_compile', 202 parser.add_argument('--cross_compile', 339 help=('Sets make\' 203 help=('Sets make\'s CROSS_COMPILE variable; it should ' 340 'be set to a 204 'be set to a toolchain path prefix (the prefix ' 341 'of gcc and 205 'of gcc and other tools in your toolchain, for ' 342 'example `sp 206 'example `sparc64-linux-gnu-` if you have the ' 343 'sparc toolc 207 'sparc toolchain installed on your system, or ' 344 '`$HOME/tool 208 '`$HOME/toolchains/microblaze/gcc-9.2.0-nolibc/microblaze-linux/bin/microblaze-linux-` ' 345 'if you have 209 'if you have downloaded the microblaze toolchain ' 346 'from the 0- 210 'from the 0-day website to a directory in your ' 347 'home direct 211 'home directory called `toolchains`).'), 348 metavar='PREFIX') !! 212 metavar='cross_compile') 349 213 350 parser.add_argument('--qemu_config', 214 parser.add_argument('--qemu_config', 351 help=('Takes a pat 215 help=('Takes a path to a path to a file containing ' 352 'a QemuArchP 216 'a QemuArchParams object.'), 353 type=str, metavar= !! 217 type=str, metavar='qemu_config') 354 << 355 parser.add_argument('--qemu_args', << 356 help='Additional Q << 357 action='append', m << 358 218 359 def add_build_opts(parser: argparse.ArgumentPa !! 219 def add_build_opts(parser) -> None: 360 parser.add_argument('--jobs', 220 parser.add_argument('--jobs', 361 help='As in the ma 221 help='As in the make command, "Specifies the number of ' 362 'jobs (commands) t 222 'jobs (commands) to run simultaneously."', 363 type=int, default= !! 223 type=int, default=8, metavar='jobs') 364 224 365 def add_exec_opts(parser: argparse.ArgumentPar !! 225 def add_exec_opts(parser) -> None: 366 parser.add_argument('--timeout', 226 parser.add_argument('--timeout', 367 help='maximum numb 227 help='maximum number of seconds to allow for all tests ' 368 'to run. This does 228 'to run. This does not include time taken to build the ' 369 'tests.', 229 'tests.', 370 type=int, 230 type=int, 371 default=300, 231 default=300, 372 metavar='SECONDS') !! 232 metavar='timeout') 373 parser.add_argument('filter_glob', 233 parser.add_argument('filter_glob', 374 help='Filter which !! 234 help='maximum number of seconds to allow for all tests ' 375 'boot-time, e.g. l !! 235 'to run. This does not include time taken to build the ' >> 236 'tests.', 376 type=str, 237 type=str, 377 nargs='?', 238 nargs='?', 378 default='', 239 default='', 379 metavar='filter_gl 240 metavar='filter_glob') 380 parser.add_argument('--filter', << 381 help='Filter KUnit << 382 'e.g. module=examp << 383 type=str, << 384 default='') << 385 parser.add_argument('--filter_action', << 386 help='If set to sk << 387 'e.g. --filter << 388 type=str, << 389 choices=['skip << 390 parser.add_argument('--kernel_args', << 391 help='Kernel comma << 392 action='append', << 393 parser.add_argument('--run_isolated', << 394 'individual suite/ << 395 'a non-hermetic te << 396 'what ran before i << 397 type=str, << 398 choices=['suite', << 399 parser.add_argument('--list_tests', he << 400 'run.', << 401 action='store_true << 402 parser.add_argument('--list_tests_attr << 403 'attributes.', << 404 action='store_true << 405 241 406 def add_parse_opts(parser: argparse.ArgumentPa !! 242 def add_parse_opts(parser) -> None: 407 parser.add_argument('--raw_output', he !! 243 parser.add_argument('--raw_output', help='don\'t format output from kernel', 408 'By default, filte !! 244 action='store_true') 409 '--raw_output=all << 410 type=str, nargs=' << 411 parser.add_argument('--json', 245 parser.add_argument('--json', 412 nargs='?', 246 nargs='?', 413 help='Prints parse !! 247 help='Stores test results in a JSON, and either ' 414 'a filename is spe !! 248 'prints to stdout or saves to file if a ' 415 type=str, const='s !! 249 'filename is specified', 416 !! 250 type=str, const='stdout', default=None) 417 << 418 def tree_from_args(cli_args: argparse.Namespac << 419 """Returns a LinuxSourceTree based on << 420 # Allow users to specify multiple argu << 421 qemu_args: List[str] = [] << 422 if cli_args.qemu_args: << 423 for arg in cli_args.qemu_args: << 424 qemu_args.extend(shlex << 425 << 426 kunitconfigs = cli_args.kunitconfig if << 427 if cli_args.alltests: << 428 # Prepend so user-specified op << 429 # --kunitconfig options to hav << 430 kunitconfigs = [kunit_kernel.A << 431 << 432 return kunit_kernel.LinuxSourceTree(cl << 433 kunitconfig_paths=kuni << 434 kconfig_add=cli_args.k << 435 arch=cli_args.arch, << 436 cross_compile=cli_args << 437 qemu_config_path=cli_a << 438 extra_qemu_args=qemu_a << 439 << 440 << 441 def run_handler(cli_args: argparse.Namespace) << 442 if not os.path.exists(cli_args.build_d << 443 os.mkdir(cli_args.build_dir) << 444 << 445 linux = tree_from_args(cli_args) << 446 request = KunitRequest(build_dir=cli_a << 447 make_o << 448 jobs=c << 449 raw_ou << 450 json=c << 451 timeou << 452 filter << 453 filter << 454 filter << 455 kernel << 456 run_is << 457 list_t << 458 list_t << 459 result = run_tests(linux, request) << 460 if result.status != KunitStatus.SUCCES << 461 sys.exit(1) << 462 << 463 << 464 def config_handler(cli_args: argparse.Namespac << 465 if cli_args.build_dir and ( << 466 not os.path.exists(cli << 467 os.mkdir(cli_args.build_dir) << 468 << 469 linux = tree_from_args(cli_args) << 470 request = KunitConfigRequest(build_dir << 471 << 472 result = config_tests(linux, request) << 473 stdout.print_with_timestamp(( << 474 'Elapsed time: %.3fs\n') % ( << 475 result.elapsed_time)) << 476 if result.status != KunitStatus.SUCCES << 477 sys.exit(1) << 478 << 479 << 480 def build_handler(cli_args: argparse.Namespace << 481 linux = tree_from_args(cli_args) << 482 request = KunitBuildRequest(build_dir= << 483 make_o << 484 jobs=c << 485 result = config_and_build_tests(linux, << 486 stdout.print_with_timestamp(( << 487 'Elapsed time: %.3fs\n') % ( << 488 result.elapsed_time)) << 489 if result.status != KunitStatus.SUCCES << 490 sys.exit(1) << 491 << 492 << 493 def exec_handler(cli_args: argparse.Namespace) << 494 linux = tree_from_args(cli_args) << 495 exec_request = KunitExecRequest(raw_ou << 496 build_ << 497 json=c << 498 timeou << 499 filter << 500 filter << 501 filter << 502 kernel << 503 run_is << 504 list_t << 505 list_t << 506 result = exec_tests(linux, exec_reques << 507 stdout.print_with_timestamp(( << 508 'Elapsed time: %.3fs\n') % (re << 509 if result.status != KunitStatus.SUCCES << 510 sys.exit(1) << 511 251 512 !! 252 def main(argv, linux=None): 513 def parse_handler(cli_args: argparse.Namespace << 514 if cli_args.file is None: << 515 sys.stdin.reconfigure(errors=' << 516 kunit_output = sys.stdin # ty << 517 else: << 518 with open(cli_args.file, 'r', << 519 kunit_output = f.read( << 520 # We know nothing about how the result << 521 metadata = kunit_json.Metadata() << 522 request = KunitParseRequest(raw_output << 523 json=c << 524 result, _ = parse_tests(request, metad << 525 if result.status != KunitStatus.SUCCES << 526 sys.exit(1) << 527 << 528 << 529 subcommand_handlers_map = { << 530 'run': run_handler, << 531 'config': config_handler, << 532 'build': build_handler, << 533 'exec': exec_handler, << 534 'parse': parse_handler << 535 } << 536 << 537 << 538 def main(argv: Sequence[str]) -> None: << 539 parser = argparse.ArgumentParser( 253 parser = argparse.ArgumentParser( 540 description='Helps wri 254 description='Helps writing and running KUnit tests.') 541 subparser = parser.add_subparsers(dest 255 subparser = parser.add_subparsers(dest='subcommand') 542 256 543 # The 'run' command will config, build 257 # The 'run' command will config, build, exec, and parse in one go. 544 run_parser = subparser.add_parser('run 258 run_parser = subparser.add_parser('run', help='Runs KUnit tests.') 545 add_common_opts(run_parser) 259 add_common_opts(run_parser) 546 add_build_opts(run_parser) 260 add_build_opts(run_parser) 547 add_exec_opts(run_parser) 261 add_exec_opts(run_parser) 548 add_parse_opts(run_parser) 262 add_parse_opts(run_parser) 549 263 550 config_parser = subparser.add_parser(' 264 config_parser = subparser.add_parser('config', 551 265 help='Ensures that .config contains all of ' 552 266 'the options in .kunitconfig') 553 add_common_opts(config_parser) 267 add_common_opts(config_parser) 554 268 555 build_parser = subparser.add_parser('b 269 build_parser = subparser.add_parser('build', help='Builds a kernel with KUnit tests') 556 add_common_opts(build_parser) 270 add_common_opts(build_parser) 557 add_build_opts(build_parser) 271 add_build_opts(build_parser) 558 272 559 exec_parser = subparser.add_parser('ex 273 exec_parser = subparser.add_parser('exec', help='Run a kernel with KUnit tests') 560 add_common_opts(exec_parser) 274 add_common_opts(exec_parser) 561 add_exec_opts(exec_parser) 275 add_exec_opts(exec_parser) 562 add_parse_opts(exec_parser) 276 add_parse_opts(exec_parser) 563 277 564 # The 'parse' option is special, as it 278 # The 'parse' option is special, as it doesn't need the kernel source 565 # (therefore there is no need for a bu 279 # (therefore there is no need for a build_dir, hence no add_common_opts) 566 # and the '--file' argument is not rel 280 # and the '--file' argument is not relevant to 'run', so isn't in 567 # add_parse_opts() 281 # add_parse_opts() 568 parse_parser = subparser.add_parser('p 282 parse_parser = subparser.add_parser('parse', 569 he 283 help='Parses KUnit results from a file, ' 570 'a 284 'and parses formatted results.') 571 add_parse_opts(parse_parser) 285 add_parse_opts(parse_parser) 572 parse_parser.add_argument('file', 286 parse_parser.add_argument('file', 573 help='Specif 287 help='Specifies the file to read results from.', 574 type=str, na 288 type=str, nargs='?', metavar='input_file') 575 289 576 cli_args = parser.parse_args(massage_a !! 290 cli_args = parser.parse_args(argv) 577 291 578 if get_kernel_root_path(): 292 if get_kernel_root_path(): 579 os.chdir(get_kernel_root_path( 293 os.chdir(get_kernel_root_path()) 580 294 581 subcomand_handler = subcommand_handler !! 295 if cli_args.subcommand == 'run': 582 !! 296 if not os.path.exists(cli_args.build_dir): 583 if subcomand_handler is None: !! 297 os.mkdir(cli_args.build_dir) >> 298 >> 299 if not linux: >> 300 linux = kunit_kernel.LinuxSourceTree(cli_args.build_dir, >> 301 kunitconfig_path=cli_args.kunitconfig, >> 302 arch=cli_args.arch, >> 303 cross_compile=cli_args.cross_compile, >> 304 qemu_config_path=cli_args.qemu_config) >> 305 >> 306 request = KunitRequest(cli_args.raw_output, >> 307 cli_args.timeout, >> 308 cli_args.jobs, >> 309 cli_args.build_dir, >> 310 cli_args.alltests, >> 311 cli_args.filter_glob, >> 312 cli_args.json, >> 313 cli_args.make_options) >> 314 result = run_tests(linux, request) >> 315 if result.status != KunitStatus.SUCCESS: >> 316 sys.exit(1) >> 317 elif cli_args.subcommand == 'config': >> 318 if cli_args.build_dir and ( >> 319 not os.path.exists(cli_args.build_dir)): >> 320 os.mkdir(cli_args.build_dir) >> 321 >> 322 if not linux: >> 323 linux = kunit_kernel.LinuxSourceTree(cli_args.build_dir, >> 324 kunitconfig_path=cli_args.kunitconfig, >> 325 arch=cli_args.arch, >> 326 cross_compile=cli_args.cross_compile, >> 327 qemu_config_path=cli_args.qemu_config) >> 328 >> 329 request = KunitConfigRequest(cli_args.build_dir, >> 330 cli_args.make_options) >> 331 result = config_tests(linux, request) >> 332 kunit_parser.print_with_timestamp(( >> 333 'Elapsed time: %.3fs\n') % ( >> 334 result.elapsed_time)) >> 335 if result.status != KunitStatus.SUCCESS: >> 336 sys.exit(1) >> 337 elif cli_args.subcommand == 'build': >> 338 if not linux: >> 339 linux = kunit_kernel.LinuxSourceTree(cli_args.build_dir, >> 340 kunitconfig_path=cli_args.kunitconfig, >> 341 arch=cli_args.arch, >> 342 cross_compile=cli_args.cross_compile, >> 343 qemu_config_path=cli_args.qemu_config) >> 344 >> 345 request = KunitBuildRequest(cli_args.jobs, >> 346 cli_args.build_dir, >> 347 cli_args.alltests, >> 348 cli_args.make_options) >> 349 result = build_tests(linux, request) >> 350 kunit_parser.print_with_timestamp(( >> 351 'Elapsed time: %.3fs\n') % ( >> 352 result.elapsed_time)) >> 353 if result.status != KunitStatus.SUCCESS: >> 354 sys.exit(1) >> 355 elif cli_args.subcommand == 'exec': >> 356 if not linux: >> 357 linux = kunit_kernel.LinuxSourceTree(cli_args.build_dir, >> 358 kunitconfig_path=cli_args.kunitconfig, >> 359 arch=cli_args.arch, >> 360 cross_compile=cli_args.cross_compile, >> 361 qemu_config_path=cli_args.qemu_config) >> 362 >> 363 exec_request = KunitExecRequest(cli_args.timeout, >> 364 cli_args.build_dir, >> 365 cli_args.alltests, >> 366 cli_args.filter_glob) >> 367 exec_result = exec_tests(linux, exec_request) >> 368 parse_request = KunitParseRequest(cli_args.raw_output, >> 369 exec_result.result, >> 370 cli_args.build_dir, >> 371 cli_args.json) >> 372 result = parse_tests(parse_request) >> 373 kunit_parser.print_with_timestamp(( >> 374 'Elapsed time: %.3fs\n') % ( >> 375 exec_result.elapsed_time)) >> 376 if result.status != KunitStatus.SUCCESS: >> 377 sys.exit(1) >> 378 elif cli_args.subcommand == 'parse': >> 379 if cli_args.file == None: >> 380 kunit_output = sys.stdin >> 381 else: >> 382 with open(cli_args.file, 'r') as f: >> 383 kunit_output = f.read().splitlines() >> 384 request = KunitParseRequest(cli_args.raw_output, >> 385 kunit_output, >> 386 None, >> 387 cli_args.json) >> 388 result = parse_tests(request) >> 389 if result.status != KunitStatus.SUCCESS: >> 390 sys.exit(1) >> 391 else: 584 parser.print_help() 392 parser.print_help() 585 return << 586 << 587 subcomand_handler(cli_args) << 588 << 589 393 590 if __name__ == '__main__': 394 if __name__ == '__main__': 591 main(sys.argv[1:]) 395 main(sys.argv[1:])
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.