Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'linux_kselftest-kunit-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest

Pull kunit tool updates from Shuah Khan:

- terminate kernel under test on SIGINT when it catches SIGINT to make
sure the TTY isn't messed up and terminate the running kernel

- recommend --raw_output=all when KTAP header isn't found in the kernel
output, it's useful to re-run the test with --raw_output=all to find
out the reasons why the test didn't complete.

- skip stty when stdin is not a tty to avoid writing noise to stderr.

- show suites when user runs --list_suites option instead of entire
list of tests to make the output user friendly and concise.

* tag 'linux_kselftest-kunit-7.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest:
kunit: tool: Terminate kernel under test on SIGINT
kunit: tool: skip stty when stdin is not a tty
kunit: tool: Recommend --raw_output=all if no KTAP found
kunit: Add --list_suites to show suites

+98 -17
+14 -2
tools/testing/kunit/kunit.py
··· 63 63 run_isolated: Optional[str] 64 64 list_tests: bool 65 65 list_tests_attr: bool 66 + list_suites: bool 66 67 67 68 @dataclass 68 69 class KunitRequest(KunitExecRequest, KunitBuildRequest): ··· 167 166 if request.list_tests_attr: 168 167 attr_output = _list_tests_attr(linux, request) 169 168 for line in attr_output: 169 + print(line.rstrip()) 170 + return KunitResult(status=KunitStatus.SUCCESS, elapsed_time=0.0) 171 + if request.list_suites: 172 + tests = _list_tests(linux, request) 173 + output = _suites_from_test_list(tests) 174 + for line in output: 170 175 print(line.rstrip()) 171 176 return KunitResult(status=KunitStatus.SUCCESS, elapsed_time=0.0) 172 177 if request.run_isolated: ··· 445 438 parser.add_argument('--list_tests_attr', help='If set, list all tests and test ' 446 439 'attributes.', 447 440 action='store_true') 441 + parser.add_argument('--list_suites', help='If set, list all suites that will be ' 442 + 'run.', 443 + action='store_true') 448 444 449 445 def add_parse_opts(parser: argparse.ArgumentParser) -> None: 450 446 parser.add_argument('--raw_output', help='If set don\'t parse output from kernel. ' ··· 511 501 kernel_args=cli_args.kernel_args, 512 502 run_isolated=cli_args.run_isolated, 513 503 list_tests=cli_args.list_tests, 514 - list_tests_attr=cli_args.list_tests_attr) 504 + list_tests_attr=cli_args.list_tests_attr, 505 + list_suites=cli_args.list_suites) 515 506 result = run_tests(linux, request) 516 507 if result.status != KunitStatus.SUCCESS: 517 508 sys.exit(1) ··· 561 550 kernel_args=cli_args.kernel_args, 562 551 run_isolated=cli_args.run_isolated, 563 552 list_tests=cli_args.list_tests, 564 - list_tests_attr=cli_args.list_tests_attr) 553 + list_tests_attr=cli_args.list_tests_attr, 554 + list_suites=cli_args.list_suites) 565 555 result = exec_tests(linux, exec_request) 566 556 stdout.print_with_timestamp(( 567 557 'Elapsed time: %.3fs\n') % (result.elapsed_time))
+27 -11
tools/testing/kunit/kunit_kernel.py
··· 16 16 import signal 17 17 import sys 18 18 import threading 19 - from typing import Iterator, List, Optional, Tuple 19 + from typing import Iterator, List, Optional, Tuple, Any 20 20 from types import FrameType 21 21 22 22 import kunit_config ··· 265 265 if kconfig_add: 266 266 kconfig = kunit_config.parse_from_string('\n'.join(kconfig_add)) 267 267 self._kconfig.merge_in_entries(kconfig) 268 + self._process : Optional[subprocess.Popen[Any]] = None 268 269 269 270 def arch(self) -> str: 270 271 return self._arch ··· 346 345 return False 347 346 return self.validate_config(build_dir) 348 347 348 + def _restore_terminal_if_tty(self) -> None: 349 + # stty requires a controlling terminal; skip headless runs. 350 + if sys.stdin is None or not sys.stdin.isatty(): 351 + return 352 + subprocess.call(['stty', 'sane']) 353 + 349 354 def run_kernel(self, args: Optional[List[str]]=None, build_dir: str='', filter_glob: str='', filter: str='', filter_action: Optional[str]=None, timeout: Optional[int]=None) -> Iterator[str]: 350 355 # Copy to avoid mutating the caller-supplied list. exec_tests() reuses 351 356 # the same args across repeated run_kernel() calls (e.g. --run_isolated), ··· 365 358 args.append('kunit.filter_action=' + filter_action) 366 359 args.append('kunit.enable=1') 367 360 368 - process = self._ops.start(args, build_dir) 369 - assert process.stdout is not None # tell mypy it's set 361 + self._process = self._ops.start(args, build_dir) 362 + assert self._process is not None # tell mypy it's set 363 + assert self._process.stdout is not None # tell mypy it's set 370 364 371 365 # Enforce the timeout in a background thread. 372 366 def _wait_proc() -> None: 373 367 try: 374 - process.wait(timeout=timeout) 368 + if self._process: 369 + self._process.wait(timeout=timeout) 375 370 except Exception as e: 376 371 print(e) 377 - process.terminate() 378 - process.wait() 372 + if self._process: 373 + self._process.terminate() 374 + self._process.wait() 379 375 waiter = threading.Thread(target=_wait_proc) 380 376 waiter.start() 381 377 382 378 output = open(get_outfile_path(build_dir), 'w') 383 379 try: 384 380 # Tee the output to the file and to our caller in real time. 385 - for line in process.stdout: 381 + for line in self._process.stdout: 386 382 output.write(line) 387 383 yield line 388 384 # This runs even if our caller doesn't consume every line. 389 385 finally: 390 386 # Flush any leftover output to the file 391 - output.write(process.stdout.read()) 387 + if self._process: 388 + if self._process.stdout: 389 + output.write(self._process.stdout.read()) 390 + self._process.stdout.close() 391 + self._process = None 392 392 output.close() 393 - process.stdout.close() 394 393 395 394 waiter.join() 396 - subprocess.call(['stty', 'sane']) 395 + self._restore_terminal_if_tty() 397 396 398 397 def signal_handler(self, unused_sig: int, unused_frame: Optional[FrameType]) -> None: 399 398 logging.error('Build interruption occurred. Cleaning console.') 400 - subprocess.call(['stty', 'sane']) 399 + if self._process: 400 + self._process.terminate() 401 + self._process.wait() 402 + self._restore_terminal_if_tty()
+2 -1
tools/testing/kunit/kunit_parser.py
··· 857 857 test = Test() 858 858 if not lines: 859 859 test.name = '<missing>' 860 - test.add_error(printer, 'Could not find any KTAP output. Did any KUnit tests run?') 860 + test.add_error(printer, 'Could not find any KTAP output. Did any KUnit tests run?\n' + 861 + 'Try running with the --raw_output=all option to see any log messages.') 861 862 test.status = TestStatus.FAILURE_TO_PARSE_TESTS 862 863 else: 863 864 test = parse_test(lines, 0, [], False, printer)
+55 -3
tools/testing/kunit/kunit_tool_test.py
··· 529 529 self.assertIn('kunit.filter_glob=suite.test1', start_calls[0]) 530 530 self.assertIn('kunit.filter_glob=suite.test2', start_calls[1]) 531 531 532 + def test_run_kernel_skips_terminal_reset_without_tty(self): 533 + def fake_start(unused_args, unused_build_dir): 534 + return subprocess.Popen(['printf', 'KTAP version 1\n'], 535 + text=True, stdout=subprocess.PIPE) 536 + 537 + non_tty_stdin = mock.Mock() 538 + non_tty_stdin.isatty.return_value = False 539 + 540 + with tempfile.TemporaryDirectory('') as build_dir: 541 + tree = kunit_kernel.LinuxSourceTree(build_dir, kunitconfig_paths=[os.devnull]) 542 + with mock.patch.object(tree._ops, 'start', side_effect=fake_start), \ 543 + mock.patch.object(kunit_kernel.sys, 'stdin', non_tty_stdin), \ 544 + mock.patch.object(kunit_kernel.subprocess, 'call') as mock_call: 545 + for _ in tree.run_kernel(build_dir=build_dir): 546 + pass 547 + 548 + mock_call.assert_not_called() 549 + 550 + def test_signal_handler_skips_terminal_reset_without_tty(self): 551 + non_tty_stdin = mock.Mock() 552 + non_tty_stdin.isatty.return_value = False 553 + tree = kunit_kernel.LinuxSourceTree('', kunitconfig_paths=[os.devnull]) 554 + 555 + with mock.patch.object(kunit_kernel.sys, 'stdin', non_tty_stdin), \ 556 + mock.patch.object(kunit_kernel.subprocess, 'call') as mock_call, \ 557 + mock.patch.object(kunit_kernel.logging, 'error') as mock_error: 558 + tree.signal_handler(signal.SIGINT, None) 559 + mock_error.assert_called_once() 560 + mock_call.assert_not_called() 561 + 562 + def test_signal_handler_resets_terminal_with_tty(self): 563 + tty_stdin = mock.Mock() 564 + tty_stdin.isatty.return_value = True 565 + tree = kunit_kernel.LinuxSourceTree('', kunitconfig_paths=[os.devnull]) 566 + 567 + with mock.patch.object(kunit_kernel.sys, 'stdin', tty_stdin), \ 568 + mock.patch.object(kunit_kernel.subprocess, 'call') as mock_call, \ 569 + mock.patch.object(kunit_kernel.logging, 'error') as mock_error: 570 + tree.signal_handler(signal.SIGINT, None) 571 + mock_error.assert_called_once() 572 + mock_call.assert_called_once_with(['stty', 'sane']) 573 + 532 574 def test_build_reconfig_no_config(self): 533 575 with tempfile.TemporaryDirectory('') as build_dir: 534 576 with open(kunit_kernel.get_kunitconfig_path(build_dir), 'w') as f: ··· 923 881 self.linux_source_mock.run_kernel.return_value = ['TAP version 14', 'init: random output'] + want 924 882 925 883 got = kunit._list_tests(self.linux_source_mock, 926 - kunit.KunitExecRequest(None, None, False, False, '.kunit', 300, 'suite*', '', None, None, 'suite', False, False)) 884 + kunit.KunitExecRequest(None, None, False, False, '.kunit', 300, 'suite*', '', None, None, 'suite', False, False, False)) 927 885 self.assertEqual(got, want) 928 886 # Should respect the user's filter glob when listing tests. 929 887 self.linux_source_mock.run_kernel.assert_called_once_with( ··· 936 894 937 895 # Should respect the user's filter glob when listing tests. 938 896 mock_tests.assert_called_once_with(mock.ANY, 939 - kunit.KunitExecRequest(None, None, False, False, '.kunit', 300, 'suite*.test*', '', None, None, 'suite', False, False)) 897 + kunit.KunitExecRequest(None, None, False, False, '.kunit', 300, 'suite*.test*', '', None, None, 'suite', False, False, False)) 940 898 self.linux_source_mock.run_kernel.assert_has_calls([ 941 899 mock.call(args=None, build_dir='.kunit', filter_glob='suite.test*', filter='', filter_action=None, timeout=300), 942 900 mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test*', filter='', filter_action=None, timeout=300), ··· 949 907 950 908 # Should respect the user's filter glob when listing tests. 951 909 mock_tests.assert_called_once_with(mock.ANY, 952 - kunit.KunitExecRequest(None, None, False, False, '.kunit', 300, 'suite*', '', None, None, 'test', False, False)) 910 + kunit.KunitExecRequest(None, None, False, False, '.kunit', 300, 'suite*', '', None, None, 'test', False, False, False)) 953 911 self.linux_source_mock.run_kernel.assert_has_calls([ 954 912 mock.call(args=None, build_dir='.kunit', filter_glob='suite.test1', filter='', filter_action=None, timeout=300), 955 913 mock.call(args=None, build_dir='.kunit', filter_glob='suite.test2', filter='', filter_action=None, timeout=300), 956 914 mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test1', filter='', filter_action=None, timeout=300), 957 915 ]) 916 + 917 + @mock.patch.object(kunit, '_list_tests') 918 + @mock.patch.object(sys, 'stdout', new_callable=io.StringIO) 919 + def test_list_suites(self, mock_stdout, mock_tests): 920 + mock_tests.return_value = ['suite.test1', 'suite.test2', 'suite2.test1'] 921 + kunit.main(['run', '--list_suites']) 922 + 923 + want = ['suite', 'suite2'] 924 + output = mock_stdout.getvalue().split() 925 + self.assertEqual(output, want) 958 926 959 927 @mock.patch.object(sys, 'stdout', new_callable=io.StringIO) 960 928 def test_list_cmds(self, mock_stdout):