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 !! 19 from typing import Iterable, Sequence 22 20 >> 21 import kunit_config 23 import kunit_json 22 import kunit_json 24 import kunit_kernel 23 import kunit_kernel 25 import kunit_parser 24 import kunit_parser 26 from kunit_printer import stdout !! 25 >> 26 KunitResult = namedtuple('KunitResult', ['status','result','elapsed_time']) >> 27 >> 28 KunitConfigRequest = namedtuple('KunitConfigRequest', >> 29 ['build_dir', 'make_options']) >> 30 KunitBuildRequest = namedtuple('KunitBuildRequest', >> 31 ['jobs', 'build_dir', 'alltests', >> 32 'make_options']) >> 33 KunitExecRequest = namedtuple('KunitExecRequest', >> 34 ['timeout', 'build_dir', 'alltests', >> 35 'filter_glob', 'kernel_args']) >> 36 KunitParseRequest = namedtuple('KunitParseRequest', >> 37 ['raw_output', 'input_data', 'build_dir', 'json']) >> 38 KunitRequest = namedtuple('KunitRequest', ['raw_output','timeout', 'jobs', >> 39 'build_dir', 'alltests', 'filter_glob', >> 40 'kernel_args', 'json', 'make_options']) >> 41 >> 42 KernelDirectoryPath = sys.argv[0].split('tools/testing/kunit/')[0] 27 43 28 class KunitStatus(Enum): 44 class KunitStatus(Enum): 29 SUCCESS = auto() 45 SUCCESS = auto() 30 CONFIG_FAILURE = auto() 46 CONFIG_FAILURE = auto() 31 BUILD_FAILURE = auto() 47 BUILD_FAILURE = auto() 32 TEST_FAILURE = auto() 48 TEST_FAILURE = auto() 33 49 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: 50 def get_kernel_root_path() -> str: 71 path = sys.argv[0] if not __file__ els 51 path = sys.argv[0] if not __file__ else __file__ 72 parts = os.path.realpath(path).split(' 52 parts = os.path.realpath(path).split('tools/testing/kunit') 73 if len(parts) != 2: 53 if len(parts) != 2: 74 sys.exit(1) 54 sys.exit(1) 75 return parts[0] 55 return parts[0] 76 56 77 def config_tests(linux: kunit_kernel.LinuxSour 57 def config_tests(linux: kunit_kernel.LinuxSourceTree, 78 request: KunitConfigRequest) 58 request: KunitConfigRequest) -> KunitResult: 79 stdout.print_with_timestamp('Configuri !! 59 kunit_parser.print_with_timestamp('Configuring KUnit Kernel ...') 80 60 81 config_start = time.time() 61 config_start = time.time() 82 success = linux.build_reconfig(request 62 success = linux.build_reconfig(request.build_dir, request.make_options) 83 config_end = time.time() 63 config_end = time.time() 84 status = KunitStatus.SUCCESS if succes !! 64 if not success: 85 return KunitResult(status, config_end !! 65 return KunitResult(KunitStatus.CONFIG_FAILURE, >> 66 'could not configure kernel', >> 67 config_end - config_start) >> 68 return KunitResult(KunitStatus.SUCCESS, >> 69 'configured kernel successfully', >> 70 config_end - config_start) 86 71 87 def build_tests(linux: kunit_kernel.LinuxSourc 72 def build_tests(linux: kunit_kernel.LinuxSourceTree, 88 request: KunitBuildRequest) -> 73 request: KunitBuildRequest) -> KunitResult: 89 stdout.print_with_timestamp('Building !! 74 kunit_parser.print_with_timestamp('Building KUnit Kernel ...') 90 75 91 build_start = time.time() 76 build_start = time.time() 92 success = linux.build_kernel(request.j !! 77 success = linux.build_kernel(request.alltests, >> 78 request.jobs, 93 request.b 79 request.build_dir, 94 request.m 80 request.make_options) 95 build_end = time.time() 81 build_end = time.time() 96 status = KunitStatus.SUCCESS if succes !! 82 if not success: 97 return KunitResult(status, build_end - !! 83 return KunitResult(KunitStatus.BUILD_FAILURE, >> 84 'could not build kernel', >> 85 build_end - build_start) >> 86 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) >> 93 >> 94 def exec_tests(linux: kunit_kernel.LinuxSourceTree, >> 95 request: KunitExecRequest) -> KunitResult: >> 96 kunit_parser.print_with_timestamp('Starting KUnit Kernel ...') >> 97 test_start = time.time() >> 98 result = linux.run_kernel( >> 99 args=request.kernel_args, >> 100 timeout=None if request.alltests else request.timeout, >> 101 filter_glob=request.filter_glob, >> 102 build_dir=request.build_dir) >> 103 >> 104 test_end = time.time() >> 105 >> 106 return KunitResult(KunitStatus.SUCCESS, >> 107 result, >> 108 test_end - test_start) 98 109 99 def config_and_build_tests(linux: kunit_kernel !! 110 def parse_tests(request: KunitParseRequest) -> KunitResult: 100 request: KunitBuild << 101 config_result = config_tests(linux, re << 102 if config_result.status != KunitStatus << 103 return config_result << 104 << 105 return build_tests(linux, request) << 106 << 107 def _list_tests(linux: kunit_kernel.LinuxSourc << 108 args = ['kunit.action=list'] << 109 << 110 if request.kernel_args: << 111 args.extend(request.kernel_arg << 112 << 113 output = linux.run_kernel(args=args, << 114 timeout=request.tim << 115 filter_glob=request << 116 filter=request.filt << 117 filter_action=reque << 118 build_dir=request.b << 119 lines = kunit_parser.extract_tap_lines << 120 # Hack! Drop the dummy TAP version hea << 121 lines.pop() << 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 << 219 def parse_tests(request: KunitParseRequest, me << 220 parse_start = time.time() 111 parse_start = time.time() 221 112 222 if request.raw_output: !! 113 test_result = kunit_parser.TestResult(kunit_parser.TestStatus.SUCCESS, 223 # Treat unparsed results as on !! 114 [], 224 fake_test = kunit_parser.Test( !! 115 'Tests not Parsed.') 225 fake_test.status = kunit_parse << 226 fake_test.counts.passed = 1 << 227 116 228 output: Iterable[str] = input_ !! 117 if request.raw_output: >> 118 output: Iterable[str] = request.input_data 229 if request.raw_output == 'all' 119 if request.raw_output == 'all': 230 pass 120 pass 231 elif request.raw_output == 'ku 121 elif request.raw_output == 'kunit': 232 output = kunit_parser. 122 output = kunit_parser.extract_tap_lines(output) >> 123 else: >> 124 print(f'Unknown --raw_output option "{request.raw_output}"', file=sys.stderr) 233 for line in output: 125 for line in output: 234 print(line.rstrip()) 126 print(line.rstrip()) 235 parse_time = time.time() - par << 236 return KunitResult(KunitStatus << 237 << 238 127 239 # Actually parse the test results. !! 128 else: 240 test = kunit_parser.parse_run_tests(in !! 129 test_result = kunit_parser.parse_run_tests(request.input_data) 241 parse_time = time.time() - parse_start !! 130 parse_end = time.time() 242 131 243 if request.json: 132 if request.json: 244 json_str = kunit_json.get_json !! 133 json_obj = kunit_json.get_json_result( 245 test=t !! 134 test_result=test_result, 246 metada !! 135 def_config='kunit_defconfig', >> 136 build_dir=request.build_dir, >> 137 json_path=request.json) 247 if request.json == 'stdout': 138 if request.json == 'stdout': 248 print(json_str) !! 139 print(json_obj) 249 else: << 250 with open(request.json << 251 f.write(json_s << 252 stdout.print_with_time << 253 os.path.abspat << 254 140 255 if test.status != kunit_parser.TestSta !! 141 if test_result.status != kunit_parser.TestStatus.SUCCESS: 256 return KunitResult(KunitStatus !! 142 return KunitResult(KunitStatus.TEST_FAILURE, test_result, >> 143 parse_end - parse_start) 257 144 258 return KunitResult(KunitStatus.SUCCESS !! 145 return KunitResult(KunitStatus.SUCCESS, test_result, >> 146 parse_end - parse_start) 259 147 260 def run_tests(linux: kunit_kernel.LinuxSourceT 148 def run_tests(linux: kunit_kernel.LinuxSourceTree, 261 request: KunitRequest) -> KunitR 149 request: KunitRequest) -> KunitResult: 262 run_start = time.time() 150 run_start = time.time() 263 151 264 config_result = config_tests(linux, re !! 152 config_request = KunitConfigRequest(request.build_dir, >> 153 request.make_options) >> 154 config_result = config_tests(linux, config_request) 265 if config_result.status != KunitStatus 155 if config_result.status != KunitStatus.SUCCESS: 266 return config_result 156 return config_result 267 157 268 build_result = build_tests(linux, requ !! 158 build_request = KunitBuildRequest(request.jobs, request.build_dir, >> 159 request.alltests, >> 160 request.make_options) >> 161 build_result = build_tests(linux, build_request) 269 if build_result.status != KunitStatus. 162 if build_result.status != KunitStatus.SUCCESS: 270 return build_result 163 return build_result 271 164 272 exec_result = exec_tests(linux, reques !! 165 exec_request = KunitExecRequest(request.timeout, request.build_dir, >> 166 request.alltests, request.filter_glob, >> 167 request.kernel_args) >> 168 exec_result = exec_tests(linux, exec_request) >> 169 if exec_result.status != KunitStatus.SUCCESS: >> 170 return exec_result >> 171 >> 172 parse_request = KunitParseRequest(request.raw_output, >> 173 exec_result.result, >> 174 request.build_dir, >> 175 request.json) >> 176 parse_result = parse_tests(parse_request) 273 177 274 run_end = time.time() 178 run_end = time.time() 275 179 276 stdout.print_with_timestamp(( !! 180 kunit_parser.print_with_timestamp(( 277 'Elapsed time: %.3fs total, %. 181 'Elapsed time: %.3fs total, %.3fs configuring, %.3fs ' + 278 'building, %.3fs running\n') % 182 'building, %.3fs running\n') % ( 279 run_end - run_ 183 run_end - run_start, 280 config_result. 184 config_result.elapsed_time, 281 build_result.e 185 build_result.elapsed_time, 282 exec_result.el 186 exec_result.elapsed_time)) 283 return exec_result !! 187 return parse_result 284 188 285 # Problem: 189 # Problem: 286 # $ kunit.py run --json 190 # $ kunit.py run --json 287 # works as one would expect and prints the par 191 # works as one would expect and prints the parsed test results as JSON. 288 # $ kunit.py run --json suite_name 192 # $ kunit.py run --json suite_name 289 # would *not* pass suite_name as the filter_gl 193 # would *not* pass suite_name as the filter_glob and print as json. 290 # argparse will consider it to be another way 194 # argparse will consider it to be another way of writing 291 # $ kunit.py run --json=suite_name 195 # $ kunit.py run --json=suite_name 292 # i.e. it would run all tests, and dump the js 196 # i.e. it would run all tests, and dump the json to a `suite_name` file. 293 # So we hackily automatically rewrite --json = 197 # So we hackily automatically rewrite --json => --json=stdout 294 pseudo_bool_flag_defaults = { 198 pseudo_bool_flag_defaults = { 295 '--json': 'stdout', 199 '--json': 'stdout', 296 '--raw_output': 'kunit', 200 '--raw_output': 'kunit', 297 } 201 } 298 def massage_argv(argv: Sequence[str]) -> Seque 202 def massage_argv(argv: Sequence[str]) -> Sequence[str]: 299 def massage_arg(arg: str) -> str: 203 def massage_arg(arg: str) -> str: 300 if arg not in pseudo_bool_flag 204 if arg not in pseudo_bool_flag_defaults: 301 return arg 205 return arg 302 return f'{arg}={pseudo_bool_f 206 return f'{arg}={pseudo_bool_flag_defaults[arg]}' 303 return list(map(massage_arg, argv)) 207 return list(map(massage_arg, argv)) 304 208 305 def get_default_jobs() -> int: !! 209 def add_common_opts(parser) -> None: 306 return len(os.sched_getaffinity(0)) << 307 << 308 def add_common_opts(parser: argparse.ArgumentP << 309 parser.add_argument('--build_dir', 210 parser.add_argument('--build_dir', 310 help='As in the ma 211 help='As in the make command, it specifies the build ' 311 'directory.', 212 'directory.', 312 type=str, default= !! 213 type=str, default='.kunit', metavar='build_dir') 313 parser.add_argument('--make_options', 214 parser.add_argument('--make_options', 314 help='X=Y make opt 215 help='X=Y make option, can be repeated.', 315 action='append', m !! 216 action='append') 316 parser.add_argument('--alltests', 217 parser.add_argument('--alltests', 317 help='Run all KUni !! 218 help='Run all KUnit tests through allyesconfig', 318 action='store_true 219 action='store_true') 319 parser.add_argument('--kunitconfig', 220 parser.add_argument('--kunitconfig', 320 help='Path to Kco 221 help='Path to Kconfig fragment that enables KUnit tests.' 321 ' If given a dire 222 ' If given a directory, (e.g. lib/kunit), "/.kunitconfig" ' 322 'will get automa !! 223 'will get automatically appended.', 323 'blindly concaten !! 224 metavar='kunitconfig') 324 action='append', << 325 parser.add_argument('--kconfig_add', << 326 help='Additional << 327 '.kunitconfig, e. << 328 action='append', m << 329 225 330 parser.add_argument('--arch', 226 parser.add_argument('--arch', 331 help=('Specifies t 227 help=('Specifies the architecture to run tests under. ' 332 'The archite 228 'The architecture specified here must match the ' 333 'string pass 229 'string passed to the ARCH make param, ' 334 'e.g. i386, 230 'e.g. i386, x86_64, arm, um, etc. Non-UML ' 335 'architectur 231 'architectures run on QEMU.'), 336 type=str, default= !! 232 type=str, default='um', metavar='arch') 337 233 338 parser.add_argument('--cross_compile', 234 parser.add_argument('--cross_compile', 339 help=('Sets make\' 235 help=('Sets make\'s CROSS_COMPILE variable; it should ' 340 'be set to a 236 'be set to a toolchain path prefix (the prefix ' 341 'of gcc and 237 'of gcc and other tools in your toolchain, for ' 342 'example `sp 238 'example `sparc64-linux-gnu-` if you have the ' 343 'sparc toolc 239 'sparc toolchain installed on your system, or ' 344 '`$HOME/tool 240 '`$HOME/toolchains/microblaze/gcc-9.2.0-nolibc/microblaze-linux/bin/microblaze-linux-` ' 345 'if you have 241 'if you have downloaded the microblaze toolchain ' 346 'from the 0- 242 'from the 0-day website to a directory in your ' 347 'home direct 243 'home directory called `toolchains`).'), 348 metavar='PREFIX') !! 244 metavar='cross_compile') 349 245 350 parser.add_argument('--qemu_config', 246 parser.add_argument('--qemu_config', 351 help=('Takes a pat 247 help=('Takes a path to a path to a file containing ' 352 'a QemuArchP 248 'a QemuArchParams object.'), 353 type=str, metavar= !! 249 type=str, metavar='qemu_config') 354 250 355 parser.add_argument('--qemu_args', !! 251 def add_build_opts(parser) -> None: 356 help='Additional Q << 357 action='append', m << 358 << 359 def add_build_opts(parser: argparse.ArgumentPa << 360 parser.add_argument('--jobs', 252 parser.add_argument('--jobs', 361 help='As in the ma 253 help='As in the make command, "Specifies the number of ' 362 'jobs (commands) t 254 'jobs (commands) to run simultaneously."', 363 type=int, default= !! 255 type=int, default=8, metavar='jobs') 364 256 365 def add_exec_opts(parser: argparse.ArgumentPar !! 257 def add_exec_opts(parser) -> None: 366 parser.add_argument('--timeout', 258 parser.add_argument('--timeout', 367 help='maximum numb 259 help='maximum number of seconds to allow for all tests ' 368 'to run. This does 260 'to run. This does not include time taken to build the ' 369 'tests.', 261 'tests.', 370 type=int, 262 type=int, 371 default=300, 263 default=300, 372 metavar='SECONDS') !! 264 metavar='timeout') 373 parser.add_argument('filter_glob', 265 parser.add_argument('filter_glob', 374 help='Filter which !! 266 help='maximum number of seconds to allow for all tests ' 375 'boot-time, e.g. l !! 267 'to run. This does not include time taken to build the ' >> 268 'tests.', 376 type=str, 269 type=str, 377 nargs='?', 270 nargs='?', 378 default='', 271 default='', 379 metavar='filter_gl 272 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', 273 parser.add_argument('--kernel_args', 391 help='Kernel comma 274 help='Kernel command-line parameters. Maybe be repeated', 392 action='append', !! 275 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 276 406 def add_parse_opts(parser: argparse.ArgumentPa !! 277 def add_parse_opts(parser) -> None: 407 parser.add_argument('--raw_output', he !! 278 parser.add_argument('--raw_output', help='If set don\'t format output from kernel. ' 408 'By default, filte !! 279 'If set to --raw_output=kunit, filters to just KUnit output.', 409 '--raw_output=all !! 280 type=str, nargs='?', const='all', default=None) 410 type=str, nargs=' << 411 parser.add_argument('--json', 281 parser.add_argument('--json', 412 nargs='?', 282 nargs='?', 413 help='Prints parse !! 283 help='Stores test results in a JSON, and either ' 414 'a filename is spe !! 284 'prints to stdout or saves to file if a ' 415 type=str, const='s !! 285 'filename is specified', 416 !! 286 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 287 512 !! 288 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( 289 parser = argparse.ArgumentParser( 540 description='Helps wri 290 description='Helps writing and running KUnit tests.') 541 subparser = parser.add_subparsers(dest 291 subparser = parser.add_subparsers(dest='subcommand') 542 292 543 # The 'run' command will config, build 293 # The 'run' command will config, build, exec, and parse in one go. 544 run_parser = subparser.add_parser('run 294 run_parser = subparser.add_parser('run', help='Runs KUnit tests.') 545 add_common_opts(run_parser) 295 add_common_opts(run_parser) 546 add_build_opts(run_parser) 296 add_build_opts(run_parser) 547 add_exec_opts(run_parser) 297 add_exec_opts(run_parser) 548 add_parse_opts(run_parser) 298 add_parse_opts(run_parser) 549 299 550 config_parser = subparser.add_parser(' 300 config_parser = subparser.add_parser('config', 551 301 help='Ensures that .config contains all of ' 552 302 'the options in .kunitconfig') 553 add_common_opts(config_parser) 303 add_common_opts(config_parser) 554 304 555 build_parser = subparser.add_parser('b 305 build_parser = subparser.add_parser('build', help='Builds a kernel with KUnit tests') 556 add_common_opts(build_parser) 306 add_common_opts(build_parser) 557 add_build_opts(build_parser) 307 add_build_opts(build_parser) 558 308 559 exec_parser = subparser.add_parser('ex 309 exec_parser = subparser.add_parser('exec', help='Run a kernel with KUnit tests') 560 add_common_opts(exec_parser) 310 add_common_opts(exec_parser) 561 add_exec_opts(exec_parser) 311 add_exec_opts(exec_parser) 562 add_parse_opts(exec_parser) 312 add_parse_opts(exec_parser) 563 313 564 # The 'parse' option is special, as it 314 # The 'parse' option is special, as it doesn't need the kernel source 565 # (therefore there is no need for a bu 315 # (therefore there is no need for a build_dir, hence no add_common_opts) 566 # and the '--file' argument is not rel 316 # and the '--file' argument is not relevant to 'run', so isn't in 567 # add_parse_opts() 317 # add_parse_opts() 568 parse_parser = subparser.add_parser('p 318 parse_parser = subparser.add_parser('parse', 569 he 319 help='Parses KUnit results from a file, ' 570 'a 320 'and parses formatted results.') 571 add_parse_opts(parse_parser) 321 add_parse_opts(parse_parser) 572 parse_parser.add_argument('file', 322 parse_parser.add_argument('file', 573 help='Specif 323 help='Specifies the file to read results from.', 574 type=str, na 324 type=str, nargs='?', metavar='input_file') 575 325 576 cli_args = parser.parse_args(massage_a 326 cli_args = parser.parse_args(massage_argv(argv)) 577 327 578 if get_kernel_root_path(): 328 if get_kernel_root_path(): 579 os.chdir(get_kernel_root_path( 329 os.chdir(get_kernel_root_path()) 580 330 581 subcomand_handler = subcommand_handler !! 331 if cli_args.subcommand == 'run': 582 !! 332 if not os.path.exists(cli_args.build_dir): 583 if subcomand_handler is None: !! 333 os.mkdir(cli_args.build_dir) >> 334 >> 335 if not linux: >> 336 linux = kunit_kernel.LinuxSourceTree(cli_args.build_dir, >> 337 kunitconfig_path=cli_args.kunitconfig, >> 338 arch=cli_args.arch, >> 339 cross_compile=cli_args.cross_compile, >> 340 qemu_config_path=cli_args.qemu_config) >> 341 >> 342 request = KunitRequest(cli_args.raw_output, >> 343 cli_args.timeout, >> 344 cli_args.jobs, >> 345 cli_args.build_dir, >> 346 cli_args.alltests, >> 347 cli_args.filter_glob, >> 348 cli_args.kernel_args, >> 349 cli_args.json, >> 350 cli_args.make_options) >> 351 result = run_tests(linux, request) >> 352 if result.status != KunitStatus.SUCCESS: >> 353 sys.exit(1) >> 354 elif cli_args.subcommand == 'config': >> 355 if cli_args.build_dir and ( >> 356 not os.path.exists(cli_args.build_dir)): >> 357 os.mkdir(cli_args.build_dir) >> 358 >> 359 if not linux: >> 360 linux = kunit_kernel.LinuxSourceTree(cli_args.build_dir, >> 361 kunitconfig_path=cli_args.kunitconfig, >> 362 arch=cli_args.arch, >> 363 cross_compile=cli_args.cross_compile, >> 364 qemu_config_path=cli_args.qemu_config) >> 365 >> 366 request = KunitConfigRequest(cli_args.build_dir, >> 367 cli_args.make_options) >> 368 result = config_tests(linux, request) >> 369 kunit_parser.print_with_timestamp(( >> 370 'Elapsed time: %.3fs\n') % ( >> 371 result.elapsed_time)) >> 372 if result.status != KunitStatus.SUCCESS: >> 373 sys.exit(1) >> 374 elif cli_args.subcommand == 'build': >> 375 if not linux: >> 376 linux = kunit_kernel.LinuxSourceTree(cli_args.build_dir, >> 377 kunitconfig_path=cli_args.kunitconfig, >> 378 arch=cli_args.arch, >> 379 cross_compile=cli_args.cross_compile, >> 380 qemu_config_path=cli_args.qemu_config) >> 381 >> 382 request = KunitBuildRequest(cli_args.jobs, >> 383 cli_args.build_dir, >> 384 cli_args.alltests, >> 385 cli_args.make_options) >> 386 result = build_tests(linux, request) >> 387 kunit_parser.print_with_timestamp(( >> 388 'Elapsed time: %.3fs\n') % ( >> 389 result.elapsed_time)) >> 390 if result.status != KunitStatus.SUCCESS: >> 391 sys.exit(1) >> 392 elif cli_args.subcommand == 'exec': >> 393 if not linux: >> 394 linux = kunit_kernel.LinuxSourceTree(cli_args.build_dir, >> 395 kunitconfig_path=cli_args.kunitconfig, >> 396 arch=cli_args.arch, >> 397 cross_compile=cli_args.cross_compile, >> 398 qemu_config_path=cli_args.qemu_config) >> 399 >> 400 exec_request = KunitExecRequest(cli_args.timeout, >> 401 cli_args.build_dir, >> 402 cli_args.alltests, >> 403 cli_args.filter_glob, >> 404 cli_args.kernel_args) >> 405 exec_result = exec_tests(linux, exec_request) >> 406 parse_request = KunitParseRequest(cli_args.raw_output, >> 407 exec_result.result, >> 408 cli_args.build_dir, >> 409 cli_args.json) >> 410 result = parse_tests(parse_request) >> 411 kunit_parser.print_with_timestamp(( >> 412 'Elapsed time: %.3fs\n') % ( >> 413 exec_result.elapsed_time)) >> 414 if result.status != KunitStatus.SUCCESS: >> 415 sys.exit(1) >> 416 elif cli_args.subcommand == 'parse': >> 417 if cli_args.file == None: >> 418 kunit_output = sys.stdin >> 419 else: >> 420 with open(cli_args.file, 'r') as f: >> 421 kunit_output = f.read().splitlines() >> 422 request = KunitParseRequest(cli_args.raw_output, >> 423 kunit_output, >> 424 None, >> 425 cli_args.json) >> 426 result = parse_tests(request) >> 427 if result.status != KunitStatus.SUCCESS: >> 428 sys.exit(1) >> 429 else: 584 parser.print_help() 430 parser.print_help() 585 return << 586 << 587 subcomand_handler(cli_args) << 588 << 589 431 590 if __name__ == '__main__': 432 if __name__ == '__main__': 591 main(sys.argv[1:]) 433 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.