# 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 . # 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 'show PREFIX' commands for the # case where the prefix command doesn't handle unknown sub-commands. # In this case GDB will list the value of every sub-command under # PREFIX. proc make_show_prefix_re { prefix } { return "$prefix param-1:\\s+The current value of '$prefix param-1' is \"off\"\\." } # Helper proc to generate the help text that describes all of the sub # commands under PREFIX. The MODE is either 'set' or 'show'. This # output will appear for 'help MODE PREFIX' and also for 'set PREFIX'. proc make_sub_cmd_help_re { mode prefix } { if { $mode == "set" } { set word "Set" } else { set word "Show" } return \ [multi_line \ "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\\."] } # 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 } { set help_re [string_to_regexp $help_text] return \ [multi_line \ "$help_re" \ "" \ [make_sub_cmd_help_re $mode $prefix]] } # 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\"\\." } } foreach prefix { prefix-1 prefix-2 prefix-3 } { gdb_test "set $prefix" \ [make_sub_cmd_help_re "set" $prefix] gdb_test "show $prefix" \ [make_show_prefix_re $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\"\\." } } foreach prefix { prefix-4 prefix-5 prefix-6 prefix-7 } { gdb_test "set $prefix" \ [make_sub_cmd_help_re "set" $prefix] gdb_test "show $prefix" \ [make_show_prefix_re $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" gdb_test "set prefix-8" \ "^invoke_set \\(a\\): \"\" True" gdb_test "show prefix-8" \ [make_show_prefix_re "prefix-8"] gdb_test "set prefix-9" \ [make_sub_cmd_help_re "set" "prefix-9"] gdb_test "show prefix-9" \ "^invoke_show \\(b\\): \"\" True" gdb_test "set prefix-10" \ "^invoke_set \\(c\\): \"\" True" gdb_test "show prefix-10" \ "^invoke_show \\(d\\): \"\" True" } # 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" } # Create a parameter prefixm, and immediately add another prefix under # the first. The important thing here is that the second prefix is # created into an otherwise empty prefix as this triggered a bug at # one point. proc_with_prefix test_nested {} { gdb_test_multiline "Create nested parameter prefixes" \ "python" "" \ "gdb.ParameterPrefix('prefix-13', gdb.COMMAND_NONE)" "" \ "gdb.ParameterPrefix('prefix-13 prefix-14', gdb.COMMAND_NONE)" "" \ "gdb.Parameter('prefix-13 param-1', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ "gdb.Parameter('prefix-13 param-2', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ "gdb.Parameter('prefix-13 prefix-14 param-3', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ "gdb.Parameter('prefix-13 prefix-14 param-4', gdb.COMMAND_NONE, gdb.PARAM_BOOLEAN)" "" \ "end" "" gdb_test "show prefix-13 prefix-14" \ [multi_line \ "^prefix-13 prefix-14 param-3: The current value of 'prefix-13 prefix-14 param-3' is \"off\"\\." \ "prefix-13 prefix-14 param-4: The current value of 'prefix-13 prefix-14 param-4' is \"off\"\\."] gdb_test "show prefix-13" \ [multi_line \ "^prefix-13 param-1: The current value of 'prefix-13 param-1' is \"off\"\\." \ "prefix-13 param-2: The current value of 'prefix-13 param-2' is \"off\"\\." \ "prefix-13 prefix-14 param-3: The current value of 'prefix-13 prefix-14 param-3' is \"off\"\\." \ "prefix-13 prefix-14 param-4: The current value of 'prefix-13 prefix-14 param-4' is \"off\"\\."] gdb_test "set prefix-13 prefix-14" \ [multi_line \ "" \ "set prefix-13 prefix-14 param-3 -- Set the current value of 'prefix-13 prefix-14 param-3'\\." \ "set prefix-13 prefix-14 param-4 -- Set the current value of 'prefix-13 prefix-14 param-4'\\." \ "" \ ".*"] gdb_test "set prefix-13" \ [multi_line \ "" \ "set prefix-13 param-1 -- Set the current value of 'prefix-13 param-1'\\." \ "set prefix-13 param-2 -- Set the current value of 'prefix-13 param-2'\\." \ "set prefix-13 prefix-14 -- This command is not documented\\." \ "" \ ".*"] } test_basic_usage test_simple_sub_class test_prefix_with_invoke test_dont_repeat test_nested