[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <CABVgOS=Yn3K+Xzq_3tb0LCrX2eJjU5AG38uMwHaa21nXfxsjEQ@mail.gmail.com>
Date: Fri, 16 Jan 2026 17:30:22 +0800
From: David Gow <davidgow@...gle.com>
To: Ryota Sakamoto <sakamo.ryota@...il.com>
Cc: Brendan Higgins <brendan.higgins@...ux.dev>, Rae Moar <raemoar63@...il.com>,
Jonathan Corbet <corbet@....net>, linux-kernel@...r.kernel.org,
linux-kselftest@...r.kernel.org, kunit-dev@...glegroups.com,
workflows@...r.kernel.org, linux-doc@...r.kernel.org
Subject: Re: [PATCH] kunit: add bash completion
On Thu, 15 Jan 2026 at 22:54, Ryota Sakamoto <sakamo.ryota@...il.com> wrote:
>
> 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
>
> Signed-off-by: Ryota Sakamoto <sakamo.ryota@...il.com>
> ---
This is awesome!
Two small suggestions:
- Could we add './tools/testing/kunit/kunit.py' to the list of
commands? That's what's recommended in lots of documentation, emails,
etc.
- It'd be great to rebase this on top of kselftest/kunit -- there's a
conflict with your previous patch.
Otherwise, this is great!
Reviewed-by: David Gow <davidgow@...gle.com>
Cheers,
-- David
> Documentation/dev-tools/kunit/run_wrapper.rst | 9 ++++++++
> tools/testing/kunit/kunit-completion.sh | 33 +++++++++++++++++++++++++++
> tools/testing/kunit/kunit.py | 30 ++++++++++++++++++++++++
> tools/testing/kunit/kunit_tool_test.py | 21 +++++++++++++++++
> 4 files changed, 93 insertions(+)
>
> diff --git a/Documentation/dev-tools/kunit/run_wrapper.rst b/Documentation/dev-tools/kunit/run_wrapper.rst
> index 6697c71ee8ca020b8ac7e91b46e29ab082d9dea0..3c0b585dcfffbd3929d0eef1ab9376fa4f380872 100644
> --- a/Documentation/dev-tools/kunit/run_wrapper.rst
> +++ b/Documentation/dev-tools/kunit/run_wrapper.rst
> @@ -335,3 +335,12 @@ command line arguments:
>
> - ``--list_tests_attr``: If set, lists all tests that will be run and all of their
> attributes.
> +
> +Command-line completion
> +==============================
> +
> +The kunit_tool comes with a bash completion script:
> +
> +.. code-block:: bash
> +
> + source tools/testing/kunit/kunit-completion.sh
> diff --git a/tools/testing/kunit/kunit-completion.sh b/tools/testing/kunit/kunit-completion.sh
> new file mode 100644
> index 0000000000000000000000000000000000000000..3b9b68e3bc384c026f10f74b8a1df2129cb2cd50
> --- /dev/null
> +++ b/tools/testing/kunit/kunit-completion.sh
> @@ -0,0 +1,33 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# bash completion support for KUnit
> +
> +_kunit_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
> +
> +_kunit()
> +{
> + local cur prev words cword
> + _init_completion || return
> +
> + local script="${_kunit_dir}/kunit.py"
> +
> + if [[ $cword -eq 1 && "$cur" != -* ]]; then
> + local cmds=$(${script} --list-cmds 2>/dev/null)
> + COMPREPLY=($(compgen -W "${cmds}" -- "$cur"))
> + return 0
> + fi
> +
> + if [[ "$cur" == -* ]]; then
> + if [[ -n "${words[1]}" && "${words[1]}" != -* ]]; then
> + local opts=$(${script} ${words[1]} --list-opts 2>/dev/null)
> + COMPREPLY=($(compgen -W "${opts}" -- "$cur"))
> + return 0
> + else
> + local opts=$(${script} --list-opts 2>/dev/null)
> + COMPREPLY=($(compgen -W "${opts}" -- "$cur"))
> + return 0
> + fi
> + fi
> +}
> +
> +complete -o default -F _kunit kunit.py
> +complete -o default -F _kunit kunit
Can we add:
complete -o default -F _kunit ./tools/testing/kunit/kunit.py
as well?
> diff --git a/tools/testing/kunit/kunit.py b/tools/testing/kunit/kunit.py
> index cd99c1956331dbbfb06cf4ddf130db3dcf2a7c31..a5aee1eb88e65fa2387b2623642d2ee9a66db600 100755
> --- a/tools/testing/kunit/kunit.py
> +++ b/tools/testing/kunit/kunit.py
> @@ -323,6 +323,17 @@ def get_default_jobs() -> int:
> return ncpu
> raise RuntimeError("os.cpu_count() returned None")
>
> +def add_completion_opts(parser: argparse.ArgumentParser) -> None:
> + parser.add_argument('--list-opts',
> + help=argparse.SUPPRESS,
> + action='store_true')
> +
> +def add_root_opts(parser: argparse.ArgumentParser) -> None:
> + parser.add_argument('--list-cmds',
> + help=argparse.SUPPRESS,
> + action='store_true')
> + add_completion_opts(parser)
> +
> def add_common_opts(parser: argparse.ArgumentParser) -> None:
> parser.add_argument('--build_dir',
> help='As in the make command, it specifies the build '
> @@ -374,6 +385,8 @@ def add_common_opts(parser: argparse.ArgumentParser) -> None:
> help='Additional QEMU arguments, e.g. "-smp 8"',
> action='append', metavar='')
>
> + add_completion_opts(parser)
> +
> def add_build_opts(parser: argparse.ArgumentParser) -> None:
> parser.add_argument('--jobs',
> help='As in the make command, "Specifies the number of '
> @@ -569,6 +582,7 @@ subcommand_handlers_map = {
> def main(argv: Sequence[str]) -> None:
> parser = argparse.ArgumentParser(
> description='Helps writing and running KUnit tests.')
> + add_root_opts(parser)
> subparser = parser.add_subparsers(dest='subcommand')
>
> # The 'run' command will config, build, exec, and parse in one go.
> @@ -603,12 +617,28 @@ def main(argv: Sequence[str]) -> None:
> parse_parser.add_argument('file',
> help='Specifies the file to read results from.',
> type=str, nargs='?', metavar='input_file')
> + add_completion_opts(parse_parser)
>
> cli_args = parser.parse_args(massage_argv(argv))
>
> if get_kernel_root_path():
> os.chdir(get_kernel_root_path())
>
> + if cli_args.list_cmds:
> + print(" ".join(subparser.choices.keys()))
> + return
> +
> + if cli_args.list_opts:
> + target_parser = subparser.choices.get(cli_args.subcommand)
> + if not target_parser:
> + target_parser = parser
> +
> + # Accessing private attribute _option_string_actions to get
> + # the list of options. This is not a public API, but argparse
> + # does not provide a way to inspect options programmatically.
> + print(' '.join(target_parser._option_string_actions.keys()))
> + return
> +
> subcomand_handler = subcommand_handlers_map.get(cli_args.subcommand, None)
>
> if subcomand_handler is None:
> diff --git a/tools/testing/kunit/kunit_tool_test.py b/tools/testing/kunit/kunit_tool_test.py
> index bbba921e0eacb18663abfcabb2bccf330d8666f5..a7f09a6c97a473ff85e087d17c2f5faf7755b994 100755
> --- a/tools/testing/kunit/kunit_tool_test.py
> +++ b/tools/testing/kunit/kunit_tool_test.py
> @@ -11,11 +11,13 @@ from unittest import mock
>
> import tempfile, shutil # Handling test_tmpdir
>
> +import io
> import itertools
> import json
> import os
> import signal
> import subprocess
> +import sys
> from typing import Iterable
>
> import kunit_config
> @@ -855,5 +857,24 @@ class KUnitMainTest(unittest.TestCase):
> mock.call(args=None, build_dir='.kunit', filter_glob='suite2.test1', filter='', filter_action=None, timeout=300),
> ])
>
> + @mock.patch.object(sys, 'stdout', new_callable=io.StringIO)
> + def test_list_cmds(self, mock_stdout):
> + kunit.main(['--list-cmds'])
> + output = mock_stdout.getvalue()
> + output_cmds = sorted(output.split())
> + expected_cmds = sorted(['build', 'config', 'exec', 'parse', 'run'])
> + self.assertEqual(output_cmds, expected_cmds)
> +
> + @mock.patch.object(sys, 'stdout', new_callable=io.StringIO)
> + def test_run_list_opts(self, mock_stdout):
> + kunit.main(['run', '--list-opts'])
> + output = mock_stdout.getvalue()
> + output_cmds = set(output.split())
> + self.assertIn('--help', output_cmds)
> + self.assertIn('--kunitconfig', output_cmds)
> + self.assertIn('--jobs', output_cmds)
> + self.assertIn('--kernel_args', output_cmds)
> + self.assertIn('--raw_output', output_cmds)
> +
> if __name__ == '__main__':
> unittest.main()
>
> ---
> base-commit: b71e635feefc852405b14620a7fc58c4c80c0f73
> change-id: 20260114-kunit-completion-265889f59c52
>
> Best regards,
> --
> Ryota Sakamoto <sakamo.ryota@...il.com>
>
Download attachment "smime.p7s" of type "application/pkcs7-signature" (5281 bytes)
Powered by blists - more mailing lists