diff options
author | Andrew Burgess <aburgess@redhat.com> | 2025-04-13 11:26:41 +0100 |
---|---|---|
committer | Andrew Burgess <aburgess@redhat.com> | 2025-05-13 15:35:26 +0100 |
commit | ef8bee09ef09230e4247ea962698bf5a0b4892cc (patch) | |
tree | 714ebdd1367c0b00c11ee2e9abc645ec6f857938 /gdb/testsuite | |
parent | a0f6a1fd48794595e4750d581b63783fed17345f (diff) | |
download | binutils-ef8bee09ef09230e4247ea962698bf5a0b4892cc.zip binutils-ef8bee09ef09230e4247ea962698bf5a0b4892cc.tar.gz binutils-ef8bee09ef09230e4247ea962698bf5a0b4892cc.tar.bz2 |
gdb/python: new gdb.ParameterPrefix class
This commit adds a new gdb.ParameterPrefix class to GDB's Python API.
When creating multiple gdb.Parameters, it is often desirable to group
these together under a sub-command, for example, 'set print' has lots
of parameters nested under it, like 'set print address', and 'set
print symbol'. In the Python API the 'print' part of these commands
are called prefix commands, and are created using gdb.Command objects.
However, as parameters are set via the 'set ....' command list, and
shown through the 'show ....' command list, creating a prefix for a
parameter usually requires two prefix commands to be created, one for
the 'set' command, and one for the 'show' command.
This often leads to some duplication, or at the very least, each user
will end up creating their own helper class to simplify creation of
the two prefix commands.
This commit adds a new gdb.ParameterPrefix class. Creating a single
instance of this class will create both the 'set' and 'show' prefix
commands, which can then be used while creating the gdb.Parameter.
Here is an example of it in use:
gdb.ParameterPrefix('my-prefix', gdb.COMMAND_NONE)
This adds 'set my-prefix' and 'show my-prefix', both of which are
prefix commands. The user can then add gdb.Parameter objects under
these prefixes.
The gdb.ParameterPrefix initialise method also supports documentation
strings, so we can write:
gdb.ParameterPrefix('my-prefix', gdb.COMMAND_NONE,
"Configuration setting relating to my special extension.")
which will set the documentation string for the prefix command.
Also, it is possible to support prefix commands that use the `invoke`
functionality to handle unknown sub-commands. This is done by
sub-classing gdb.ParameterPrefix and overriding either 'invoke_set' or
'invoke_show' to handle the 'set' or 'show' prefix command
respectively.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
Diffstat (limited to 'gdb/testsuite')
-rw-r--r-- | gdb/testsuite/gdb.python/py-parameter-prefix.exp | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/gdb/testsuite/gdb.python/py-parameter-prefix.exp b/gdb/testsuite/gdb.python/py-parameter-prefix.exp new file mode 100644 index 0000000..aa2f84a --- /dev/null +++ b/gdb/testsuite/gdb.python/py-parameter-prefix.exp @@ -0,0 +1,285 @@ +# Copyright (C) 2025 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +# This file is part of the GDB testsuite. It tests +# gdb.ParameterPrefix. See each of the test procs for a full +# description of what is being tested. + +load_lib gdb-python.exp + +require allow_python_tests + +clean_restart + +# Helper proc to generate the output of 'help MODE PREFIX', where MODE +# will be either 'set' or 'show'. The HELP_TEXT is the expected help +# text for this prefix command, this should not be a regexp, as this +# proc converts the text to a regexp. +# +# Return a single regexp which should match the output. +proc make_help_re { mode prefix help_text } { + if { $mode == "set" } { + set word "Set" + } else { + set word "Show" + } + + set help_re [string_to_regexp $help_text] + + return \ + [multi_line \ + "$help_re" \ + "" \ + "List of \"$mode $prefix\" subcommands:" \ + "" \ + "$mode $prefix param-1 -- $word the current value of '$prefix param-1'\\." \ + "" \ + "Type \"help $mode $prefix\" followed by subcommand name for full documentation\\." \ + "Type \"apropos word\" to search for commands related to \"word\"\\." \ + "Type \"apropos -v word\" for full documentation of commands related to \"word\"\\." \ + "Command name abbreviations are allowed if unambiguous\\."] +} + +# Create gdb.ParameterPrefix without using a sub-class, both with, and +# without a doc string. For the doc string case, test single line, +# and multi-line doc strings. +proc_with_prefix test_basic_usage {} { + gdb_test_multiline "some basic ParameterPrefix usage" \ + "python" "" \ + "gdb.ParameterPrefix('prefix-1', gdb.COMMAND_NONE)" "" \ + "gdb.Parameter('prefix-1 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.Parameter('prefix-1 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.ParameterPrefix('prefix-2', gdb.COMMAND_NONE," "" \ + " \"\"\"This is prefix-2 help string.\"\"\")" "" \ + "gdb.Parameter('prefix-2 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "gdb.ParameterPrefix('prefix-3', gdb.COMMAND_NONE," "" \ + " \"\"\"This is prefix-3 help string." "" \ + " " "" \ + " This help text spans multiple lines.\"\"\")" "" \ + "gdb.Parameter('prefix-3 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "end" + + foreach mode { "set" "show" } { + gdb_test "help $mode prefix-1" \ + [make_help_re $mode "prefix-1" \ + "This command is not documented."] + + gdb_test "help $mode prefix-2" \ + [make_help_re $mode "prefix-2" \ + "This is prefix-2 help string."] + + gdb_test "help $mode prefix-3" \ + [make_help_re $mode "prefix-3" \ + [multi_line \ + "This is prefix-3 help string." \ + "" \ + "This help text spans multiple lines."]] + + foreach prefix { prefix-1 prefix-2 prefix-3 } { + gdb_test "$mode $prefix xxx" \ + "^Undefined $mode $prefix command: \"xxx\"\\. Try \"help $mode $prefix\"\\." + } + } +} + +# Create a sub-class of gdb.ParameterPrefix, but don't do anything +# particularly interesting. Again test the with and without +# documentation string cases. +proc_with_prefix test_simple_sub_class {} { + gdb_test_multiline "some basic ParameterPrefix usage" \ + "python" "" \ + "class BasicParamPrefix(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + "BasicParamPrefix('prefix-4')" "" \ + "gdb.Parameter('prefix-4 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class BasicParamPrefixWithSingleLineDoc(gdb.ParameterPrefix):" "" \ + " \"\"\"This is a single line doc string.\"\"\"" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + "BasicParamPrefixWithSingleLineDoc('prefix-5')" "" \ + "gdb.Parameter('prefix-5 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class BasicParamPrefixWithMultiLineDoc(gdb.ParameterPrefix):" "" \ + " \"\"\"This is a multi line doc string." "" \ + " " "" \ + " The rest of the doc string is here.\"\"\"" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + "BasicParamPrefixWithMultiLineDoc('prefix-6')" "" \ + "gdb.Parameter('prefix-6 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class BasicParamPrefixWithDocParameter(gdb.ParameterPrefix):" "" \ + " \"\"\"This is an unsused doc string.\"\"\"" "" \ + " def __init__(self, name, doc):" "" \ + " super().__init__(name, gdb.COMMAND_NONE, doc)" "" \ + "BasicParamPrefixWithDocParameter('prefix-7'," "" \ + " \"\"\"The doc string text is here.\"\"\")" "" \ + "gdb.Parameter('prefix-7 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "end" + + foreach mode { "set" "show" } { + gdb_test "help $mode prefix-4" \ + [make_help_re $mode "prefix-4" \ + "This command is not documented."] + + gdb_test "help $mode prefix-5" \ + [make_help_re $mode "prefix-5" \ + "This is a single line doc string."] + + gdb_test "help $mode prefix-6" \ + [make_help_re $mode "prefix-6" \ + [multi_line \ + "This is a multi line doc string." \ + "" \ + "The rest of the doc string is here."]] + + gdb_test "help $mode prefix-7" \ + [make_help_re $mode "prefix-7" \ + "The doc string text is here."] + + foreach prefix { prefix-4 prefix-5 prefix-6 prefix-7 } { + gdb_test "$mode $prefix xxx" \ + "^Undefined $mode $prefix command: \"xxx\"\\. Try \"help $mode $prefix\"\\." + } + } +} + +# Create a sub-class of gdb.ParameterPrefix, and make use of +# 'invoke_set' and 'invoke_show'. Test that the invoke method is +# executed when expected, and that, by default, these invoke methods +# repeat when the user issues an empty command. +proc_with_prefix test_prefix_with_invoke {} { + gdb_test_multiline "ParameterPrefix with invoke_set" \ + "python" "" \ + "class PrefixWithInvokeSet(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + " def invoke_set(self, args, from_tty):" "" \ + " print(f\"invoke_set (a): \\\"{args}\\\" {from_tty}\")" "" \ + "PrefixWithInvokeSet('prefix-8')" "" \ + "gdb.Parameter('prefix-8 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class PrefixWithInvokeShow(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + " def invoke_show(self, args, from_tty):" "" \ + " print(f\"invoke_show (b): \\\"{args}\\\" {from_tty}\")" "" \ + "PrefixWithInvokeShow('prefix-9')" "" \ + "gdb.Parameter('prefix-9 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class PrefixWithBothInvoke(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + " def invoke_set(self, args, from_tty):" "" \ + " print(f\"invoke_set (c): \\\"{args}\\\" {from_tty}\")" "" \ + " def invoke_show(self, args, from_tty):" "" \ + " print(f\"invoke_show (d): \\\"{args}\\\" {from_tty}\")" "" \ + "PrefixWithBothInvoke('prefix-10')" "" \ + "gdb.Parameter('prefix-10 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "end" + + gdb_test "set prefix-8 xxx yyy" \ + "^invoke_set \\(a\\): \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^\r\ninvoke_set \\(a\\): \"xxx yyy\" True" \ + "repeat set prefix-8 xxx yyy" + + gdb_test "show prefix-8 xxx yyy" \ + "^Undefined show prefix-8 command: \"xxx yyy\"\\. Try \"help show prefix-8\"\\." + + gdb_test "set prefix-9 xxx yyy" \ + "^Undefined set prefix-9 command: \"xxx yyy\"\\. Try \"help set prefix-9\"\\." + + gdb_test "show prefix-9 xxx yyy" \ + "^invoke_show \\(b\\): \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^\r\ninvoke_show \\(b\\): \"xxx yyy\" True" \ + "repeat show prefix-9 xxx yyy" + + gdb_test "set prefix-10 xxx yyy" \ + "^invoke_set \\(c\\): \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^\r\ninvoke_set \\(c\\): \"xxx yyy\" True" \ + "repeat set prefix-10 xxx yyy" + + gdb_test "show prefix-10 xxx yyy" \ + "^invoke_show \\(d\\): \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^\r\ninvoke_show \\(d\\): \"xxx yyy\" True" \ + "repeat show prefix-10 xxx yyy" +} + +# Create ParameterPrefix sub-classes that make use of the +# dont_repeat() method. Check that the relevant set/show invoke +# callback doesn't repeat when an empty command is used. +proc_with_prefix test_dont_repeat {} { + gdb_test_multiline "ParameterPrefix with invoke_set and dont_repeat" \ + "python" "" \ + "class PrefixWithInvokeAndDoNotRepeatSet(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + " def invoke_set(self, args, from_tty):" "" \ + " self.dont_repeat()" "" \ + " print(f\"invoke_set: \\\"{args}\\\" {from_tty}\")" "" \ + " def invoke_show(self, args, from_tty):" "" \ + " print(f\"invoke_show: \\\"{args}\\\" {from_tty}\")" "" \ + "PrefixWithInvokeAndDoNotRepeatSet('prefix-11')" "" \ + "gdb.Parameter('prefix-11 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "class PrefixWithInvokeAndDoNotRepeatShow(gdb.ParameterPrefix):" "" \ + " def __init__(self, name):" "" \ + " super().__init__(name, gdb.COMMAND_NONE)" "" \ + " def invoke_set(self, args, from_tty):" "" \ + " print(f\"invoke_set: \\\"{args}\\\" {from_tty}\")" "" \ + " def invoke_show(self, args, from_tty):" "" \ + " self.dont_repeat()" "" \ + " print(f\"invoke_show: \\\"{args}\\\" {from_tty}\")" "" \ + "PrefixWithInvokeAndDoNotRepeatShow('prefix-12')" "" \ + "gdb.Parameter('prefix-12 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ + "end" + + gdb_test "set prefix-11 xxx yyy" \ + "^invoke_set: \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^" \ + "repeat set prefix-11 xxx yyy" + + gdb_test "show prefix-11 xxx yyy" \ + "^invoke_show: \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "invoke_show: \"xxx yyy\" True" \ + "repeat show prefix-11 xxx yyy" + + gdb_test "set prefix-12 xxx yyy" \ + "^invoke_set: \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^\r\ninvoke_set: \"xxx yyy\" True" \ + "repeat set prefix-12 xxx yyy" + + gdb_test "show prefix-12 xxx yyy" \ + "^invoke_show: \"xxx yyy\" True" + + send_gdb "\n" + gdb_test "" "^" \ + "repeat show prefix-12 xxx yyy" +} + +test_basic_usage +test_simple_sub_class +test_prefix_with_invoke +test_dont_repeat |