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