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.

kunit: add bash completion

Currently, kunit.py has many subcommands and options, making it difficult
to remember them without checking the help message.

Add --list-cmds and --list-opts to kunit.py to get available commands and
options, use those outputs in kunit-completion.sh to show completion.

This implementation is similar to perf and tools/perf/perf-completion.sh.

Example output:
$ source tools/testing/kunit/kunit-completion.sh
$ ./tools/testing/kunit/kunit.py [TAB][TAB]
build config exec parse run
$ ./tools/testing/kunit/kunit.py run --k[TAB][TAB]
--kconfig_add --kernel_args --kunitconfig

Link: https://lore.kernel.org/r/20260117-kunit-completion-v2-1-cabd127d0801@gmail.com
Reviewed-by: David Gow <davidgow@google.com>
Signed-off-by: Ryota Sakamoto <sakamo.ryota@gmail.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>

authored by

Ryota Sakamoto and committed by
Shuah Khan
db0c35ca f126d688

+94
+9
Documentation/dev-tools/kunit/run_wrapper.rst
··· 335 335 336 336 - ``--list_tests_attr``: If set, lists all tests that will be run and all of their 337 337 attributes. 338 + 339 + Command-line completion 340 + ============================== 341 + 342 + The kunit_tool comes with a bash completion script: 343 + 344 + .. code-block:: bash 345 + 346 + source tools/testing/kunit/kunit-completion.sh
+34
tools/testing/kunit/kunit-completion.sh
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # bash completion support for KUnit 3 + 4 + _kunit_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) 5 + 6 + _kunit() 7 + { 8 + local cur prev words cword 9 + _init_completion || return 10 + 11 + local script="${_kunit_dir}/kunit.py" 12 + 13 + if [[ $cword -eq 1 && "$cur" != -* ]]; then 14 + local cmds=$(${script} --list-cmds 2>/dev/null) 15 + COMPREPLY=($(compgen -W "${cmds}" -- "$cur")) 16 + return 0 17 + fi 18 + 19 + if [[ "$cur" == -* ]]; then 20 + if [[ -n "${words[1]}" && "${words[1]}" != -* ]]; then 21 + local opts=$(${script} ${words[1]} --list-opts 2>/dev/null) 22 + COMPREPLY=($(compgen -W "${opts}" -- "$cur")) 23 + return 0 24 + else 25 + local opts=$(${script} --list-opts 2>/dev/null) 26 + COMPREPLY=($(compgen -W "${opts}" -- "$cur")) 27 + return 0 28 + fi 29 + fi 30 + } 31 + 32 + complete -o default -F _kunit kunit.py 33 + complete -o default -F _kunit kunit 34 + complete -o default -F _kunit ./tools/testing/kunit/kunit.py
+30
tools/testing/kunit/kunit.py
··· 328 328 return os.path.join(os.environ['KBUILD_OUTPUT'], '.kunit') 329 329 return '.kunit' 330 330 331 + def add_completion_opts(parser: argparse.ArgumentParser) -> None: 332 + parser.add_argument('--list-opts', 333 + help=argparse.SUPPRESS, 334 + action='store_true') 335 + 336 + def add_root_opts(parser: argparse.ArgumentParser) -> None: 337 + parser.add_argument('--list-cmds', 338 + help=argparse.SUPPRESS, 339 + action='store_true') 340 + add_completion_opts(parser) 341 + 331 342 def add_common_opts(parser: argparse.ArgumentParser) -> None: 332 343 parser.add_argument('--build_dir', 333 344 help='As in the make command, it specifies the build ' ··· 389 378 parser.add_argument('--qemu_args', 390 379 help='Additional QEMU arguments, e.g. "-smp 8"', 391 380 action='append', metavar='') 381 + 382 + add_completion_opts(parser) 392 383 393 384 def add_build_opts(parser: argparse.ArgumentParser) -> None: 394 385 parser.add_argument('--jobs', ··· 587 574 def main(argv: Sequence[str]) -> None: 588 575 parser = argparse.ArgumentParser( 589 576 description='Helps writing and running KUnit tests.') 577 + add_root_opts(parser) 590 578 subparser = parser.add_subparsers(dest='subcommand') 591 579 592 580 # The 'run' command will config, build, exec, and parse in one go. ··· 622 608 parse_parser.add_argument('file', 623 609 help='Specifies the file to read results from.', 624 610 type=str, nargs='?', metavar='input_file') 611 + add_completion_opts(parse_parser) 625 612 626 613 cli_args = parser.parse_args(massage_argv(argv)) 627 614 628 615 if get_kernel_root_path(): 629 616 os.chdir(get_kernel_root_path()) 617 + 618 + if cli_args.list_cmds: 619 + print(" ".join(subparser.choices.keys())) 620 + return 621 + 622 + if cli_args.list_opts: 623 + target_parser = subparser.choices.get(cli_args.subcommand) 624 + if not target_parser: 625 + target_parser = parser 626 + 627 + # Accessing private attribute _option_string_actions to get 628 + # the list of options. This is not a public API, but argparse 629 + # does not provide a way to inspect options programmatically. 630 + print(' '.join(target_parser._option_string_actions.keys())) 631 + return 630 632 631 633 subcomand_handler = subcommand_handlers_map.get(cli_args.subcommand, None) 632 634
+21
tools/testing/kunit/kunit_tool_test.py
··· 11 11 12 12 import tempfile, shutil # Handling test_tmpdir 13 13 14 + import io 14 15 import itertools 15 16 import json 16 17 import os 17 18 import signal 18 19 import subprocess 20 + import sys 19 21 from typing import Iterable 20 22 21 23 import kunit_config ··· 887 885 mock.call(args=None, build_dir='.kunit', filter_glob='suite.test2', filter='', filter_action=None, timeout=300), 888 886 mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test1', filter='', filter_action=None, timeout=300), 889 887 ]) 888 + 889 + @mock.patch.object(sys, 'stdout', new_callable=io.StringIO) 890 + def test_list_cmds(self, mock_stdout): 891 + kunit.main(['--list-cmds']) 892 + output = mock_stdout.getvalue() 893 + output_cmds = sorted(output.split()) 894 + expected_cmds = sorted(['build', 'config', 'exec', 'parse', 'run']) 895 + self.assertEqual(output_cmds, expected_cmds) 896 + 897 + @mock.patch.object(sys, 'stdout', new_callable=io.StringIO) 898 + def test_run_list_opts(self, mock_stdout): 899 + kunit.main(['run', '--list-opts']) 900 + output = mock_stdout.getvalue() 901 + output_cmds = set(output.split()) 902 + self.assertIn('--help', output_cmds) 903 + self.assertIn('--kunitconfig', output_cmds) 904 + self.assertIn('--jobs', output_cmds) 905 + self.assertIn('--kernel_args', output_cmds) 906 + self.assertIn('--raw_output', output_cmds) 890 907 891 908 if __name__ == '__main__': 892 909 unittest.main()