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