# 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.Style. load_lib gdb-python.exp require allow_python_tests # Start GDB, allow styling. with_ansi_styling_terminal { clean_restart } # Check the error for unknown style names. foreach unknown_style { "unknown-style" "disassembler unknown-style" } { gdb_test "python filename_style = gdb.Style('$unknown_style')" \ [multi_line \ "Python Exception : style '$unknown_style' cannot be found\\." \ "Error occurred in Python: style '$unknown_style' cannot be found\\."] \ "attempt to create named style for '$unknown_style'" } # Check we can create a style using an abbreviated style name. gdb_test_no_output "python abbrev_style = gdb.Style('high')" \ "create style using abbreviated name 'high'" gdb_test "python print(abbrev_style)" \ "^" \ "print the 'highlight' style" gdb_test_no_output "python abbrev_style = gdb.Style('disas mnem')" \ "create style using abbreviated name 'disas mnem'" gdb_test "python print(abbrev_style)" \ "^" \ "print the 'disassembler mnemonic' style" # Creating a style using an ambiguous abbreviated name will give an error. gdb_test "python abbrev_style = gdb.Style('f')" \ [multi_line \ "Python Exception : style 'f' cannot be found\\." \ "Error occurred in Python: style 'f' cannot be found\\."] \ "create style using abbreviated name 'f'" # Check a couple of different styles can be read. The 'tui-border' is # interesting as there is no 'intensity' for this one, the gdb.Style # object will show this as gdb.INTENSITY_NORMAL. The disassembler # styles are interesting because they are two word style names, and # the comment style has a foreground and intensity set. foreach style_check { { "filename" green none NORMAL } \ { "title" none none BOLD } \ { "tui-border" cyan none NORMAL } \ { "disassembler address" blue none NORMAL } \ { "disassembler comment" white none DIM } } { set style_name [lindex $style_check 0] set fg [lindex $style_check 1] set bg [lindex $style_check 2] set intensity [lindex $style_check 3] with_test_prefix "check style $style_name" { gdb_test_no_output "python style_obj = gdb.Style('$style_name')" \ "create named style for $style_name" gdb_test "python print(style_obj.foreground)" "^$fg" \ "print foreground color" gdb_test "python print(style_obj.background)" "^$bg" \ "print background color" gdb_test "python print(style_obj.intensity == gdb.INTENSITY_$intensity)" \ "^True" "print intensity" gdb_test "python print(style_obj)" \ "^" \ "print string representation" } } # Check that the intensity of a named style with no intensity # (tui-border) cannot be changed. gdb_test_no_output "python tui_border_style = gdb.Style('tui-border')" \ "create named style for 'tui-border'" gdb_test "python tui_border_style.intensity = gdb.INTENSITY_BOLD" \ [multi_line \ "Python Exception : the intensity of style 'tui-border' is not writable\\." \ "Error occurred in Python: the intensity of style 'tui-border' is not writable\\."] # Change the attributes of a named style, check the settings update as # expected. gdb_test_no_output "python filename_style = gdb.Style('filename')" \ "create named style for 'filename'" gdb_test_no_output "python filename_style.foreground = gdb.Color('blue')" \ "assign blue to filename foreground color" gdb_test_no_output "python filename_style.background = gdb.Color('red')" \ "assign red to filename background color" gdb_test_no_output "python filename_style.intensity = gdb.INTENSITY_BOLD" # Use 'with style enabled off' so that there are no escape sequences # in the output. gdb_test "with style enabled off -- show style filename" \ [multi_line \ "style filename background: The \"filename\" style background color is: red" \ "style filename foreground: The \"filename\" style foreground color is: blue" \ "style filename intensity: The \"filename\" style display intensity is: bold"] gdb_test "python print(filename_style)" \ "^" \ "print string representation of filename_style" # Check some attempts to set the gdb.Style attributes to invalid types. foreach attr { foreground background } { gdb_test "python filename_style.$attr = None" \ [multi_line \ "Python Exception : value must be gdb.Color, not NoneType" \ "Error occurred in Python: value must be gdb.Color, not NoneType"] gdb_test "python filename_style.$attr = list()" \ [multi_line \ "Python Exception : value must be gdb.Color, not list" \ "Error occurred in Python: value must be gdb.Color, not list"] gdb_test "python filename_style.$attr = 'red'" \ [multi_line \ "Python Exception : value must be gdb.Color, not str" \ "Error occurred in Python: value must be gdb.Color, not str"] } gdb_test "python filename_style.intensity = None" \ [multi_line \ "Python Exception : value must be a Long \\(a gdb.INTENSITY constant\\), not NoneType" \ "Error occurred in Python: value must be a Long \\(a gdb.INTENSITY constant\\), not NoneType"] gdb_test "python filename_style.intensity = 'dim'" \ [multi_line \ "Python Exception : value must be a Long \\(a gdb.INTENSITY constant\\), not str" \ "Error occurred in Python: value must be a Long \\(a gdb.INTENSITY constant\\), not str"] # Check attempts to set the intensity to an integer value work as # expected. This is mostly about testing invalid integer values, but # we do also check that 0, 1, and 2 work as expected, though it is bad # practice to use the raw integer value rather than the defined # constants. set intensity_str { NORMAL BOLD DIM } for { set i -3 } { $i < 6 } { incr i } { if { $i < 0 || $i > 2 } { gdb_test "python filename_style.intensity = $i" \ [multi_line \ "Python Exception : invalid 'intensity' value $i\\." \ "Error occurred in Python: invalid 'intensity' value $i\\."] } else { gdb_test_no_output "python filename_style.intensity = $i" set str [lindex $intensity_str $i] gdb_test "python print(filename_style.intensity == gdb.INTENSITY_$str)" \ "^True" "check filename_style intensity is $str" } } # Check the filename style has changed as expected. gdb_test "python print(filename_style)" \ "^" \ "check filename_style is now dim" # Check Style.escape_sequence and Style.apply when styling is disabled. gdb_test_no_output "with style enabled off -- python print(filename_style.escape_sequence(), end='')" \ "print escape sequence when styling is off" gdb_test "with style enabled off -- python print(filename_style.apply('xxx'))" "^xxx" \ "apply style to a string when styling is off" # Now check the escape sequences are emitted as expected. gdb_test "python print(filename_style.escape_sequence())" \ "\033\\\[34;41;2;23;24;27m" \ "print escape sequence when styling is on" gdb_test "python print(filename_style.apply('xxx'))" \ "\033\\\[34;41;2;23;24;27mxxx\033\\\[m" \ "apply a style when styling is on" # Test creating a style from component parts. gdb_test_no_output "python my_style_1 = gdb.Style(gdb.Color('red'), gdb.Color('blue'), gdb.INTENSITY_BOLD)" \ "create my_style_1" gdb_test "python print(my_style_1)" \ "^" \ "check my_style_1" gdb_test_no_output "python my_style_2 = gdb.Style(background = gdb.Color('blue'))" \ "create my_style_2" gdb_test "python print(my_style_2)" \ "^" \ "check my_style_2" gdb_test_no_output "python my_style_3 = gdb.Style(intensity = gdb.INTENSITY_DIM)" \ "create my_style_3" gdb_test "python print(my_style_3)" \ "^" \ "check my_style_3" # The precise error message, about 'None' not being an integer, varies # with Python version. We just check that we get a TypeError and # assume that this is related to the argument type. gdb_test "python my_style_4 = gdb.Style(intensity = None)" \ [multi_line \ "Python Exception : \[^\r\n\]+" \ "Error occurred in Python: \[^\r\n\]+"] \ "attempt to create my_style_4" gdb_test "python my_style_5 = gdb.Style(foreground = list())" \ [multi_line \ "Python Exception : 'foreground' argument must be gdb.Color or None, not list\\." \ "Error occurred in Python: 'foreground' argument must be gdb.Color or None, not list\\."] \ "attempt to create my_style_5" gdb_test "python my_style_6 = gdb.Style(background = list())" \ [multi_line \ "Python Exception : 'background' argument must be gdb.Color or None, not list\\." \ "Error occurred in Python: 'background' argument must be gdb.Color or None, not list\\."] \ "attempt to create my_style_6" # Adjust the attributes of an unnamed style. gdb_test_no_output "python my_style_1.foreground = gdb.Color('green')" \ "change my_style_1.foreground to green" gdb_test_no_output "python my_style_1.background = gdb.Color('cyan')" \ "change my_style_1.background to cyan" gdb_test_no_output "python my_style_1.intensity = gdb.INTENSITY_DIM" \ "change my_style_1.intensity to dim" gdb_test "python print(my_style_1)" \ "^" \ "check my_style_1 after changes." # Setup some prefix commands under 'set/show style ...'. Each prefix # command is called 'new-style-X' where X is an integer; 1, 2, 3, or 4. for { set i 1 } { $i < 5 } { incr i } { gdb_test_no_output "python gdb.Command('set style new-style-$i', gdb.COMMAND_NONE, prefix = True)" \ "create set style new-style-$i" gdb_test_no_output "python gdb.Command('show style new-style-$i', gdb.COMMAND_NONE, prefix = True)" \ "create show style new-style-$i" } # A helper class for creating color settings under the 'new-style-X' # prefix commands. The NUM to the __init__ supplies the value of # 'X', and NAME is either 'foreground' or 'background'. gdb_test_multiline "Class to create color parameters" \ "python" "" \ "class color_param(gdb.Parameter):" "" \ " def __init__(self, num, name):" "" \ " super().__init__(f\"style new-style-{num} {name}\", gdb.COMMAND_NONE, gdb.PARAM_COLOR)" "" \ " self.value = gdb.Color()" "" \ "end" "" # A helper class for creating intensity settings under the new # 'new-style-X' prefix commands. The NUM in the __init__ supplies the # value of 'X'. gdb_test_multiline "Class to create intensity parameters" \ "python" "" \ "class intensity_param(gdb.Parameter):" "" \ " def __init__(self, num):" "" \ " super().__init__(f\"style new-style-{num} intensity\", gdb.COMMAND_NONE, gdb.PARAM_ENUM," "" \ " \[\"normal\", \"bold\", \"dim\"\])" "" \ " self.value = \"normal\"" "" \ "end" "" # Within 'style new-style-1' we only have a 'foreground' setting. # This will not be usable as a gdb.Style. gdb_test_no_output "python color_param(1, 'foreground')" \ "setup new-style-1 foreground" # Within 'style new-style-2' we only have a 'background' setting. # This will not be usable as a gdb.Style. gdb_test_no_output "python color_param(2, 'background')" \ "setup new-style-2 background" # Within 'style new-style-3' we have both a 'foreground' and # 'background' setting. Even though 'intensity' is missing, this is # still usable as a style. gdb_test_no_output "python color_param(3, 'foreground')" \ "setup new-style-3 foreground" gdb_test_no_output "python color_param(3, 'background')" \ "setup new-style-3 background" # Within 'style new-style-4' we have a 'foreground', 'background', and # 'intensity' setting. This is a complete style setting group. gdb_test_no_output "python color_param(4, 'foreground')" \ "setup new-style-4 foreground" gdb_test_no_output "python color_param(4, 'background')" \ "setup new-style-4 background" gdb_test_no_output "python intensity_param(4)" \ "setup new-style-4 intensity" # Trying to create a style for 'new-style-1' should fail. gdb_test "python my_style_7 = gdb.Style('new-style-1')" \ [multi_line \ "Python Exception : style 'new-style-1' missing 'background' component\\." \ "Error occurred in Python: style 'new-style-1' missing 'background' component\\."] \ "try to create style for new-style-1" # Trying to create a style for 'new-style-2' should fail. gdb_test "python my_style_8 = gdb.Style('new-style-2')" \ [multi_line \ "Python Exception : style 'new-style-2' missing 'foreground' component\\." \ "Error occurred in Python: style 'new-style-2' missing 'foreground' component\\."] \ "try to create style for new-style-2" # Trying to create a style for 'new-style-3' should succeed. gdb_test_no_output "python my_style_9 = gdb.Style('new-style-3')" \ "create a style for new-style-3" gdb_test "python print(my_style_9)" \ "^" \ "print my_style_9" # Trying to create a style for 'new-style-4' should succeed too. gdb_test_no_output "python my_style_10 = gdb.Style('new-style-4')" \ "create a style for new-style-4" gdb_test "python print(my_style_10)" \ "^" \ "print my_style_10" # Adjust the settings directly, and check the gdb.Style updates. gdb_test_no_output "set style new-style-4 intensity bold" gdb_test_no_output "set style new-style-4 foreground red" gdb_test_no_output "set style new-style-4 background blue" gdb_test "python print(my_style_10)" \ "^" \ "print my_style_10, intensity updated" # Check the reference counting on 'gdb.Style.apply' when global styling is disabled. gdb_test_no_output "python input_text = \"this is a unique string that is unlikely to appear elsewhere 12345\"" \ "setup an input string" gdb_test_no_output "with style enabled off -- python output_text = filename_style.apply(input_text)" \ "create an ouput string by applying filename_style" gdb_test_no_output "python input_text = \"a totally different string that is also, hopefully, unique\"" \ "replace the input string" gdb_test "python print(output_text)" \ "^this is a unique string that is unlikely to appear elsewhere 12345" \ "check the output_text is still valid" # Test gdb.write passing in a style. Define a helper function to # ensure all output is flushed before we return to the prompt. gdb_test_multiline "create function to call gdb.write then flush" \ "python" "" \ "def write_and_flush(*args, **kwargs):" "" \ " gdb.write(*args, **kwargs)" "" \ " gdb.write(\"\\n\")" "" \ " gdb.flush(gdb.STDOUT)" "" \ "end" "" gdb_test "python write_and_flush(\"some text\")" \ "^some text" "unstyled text, no style passed" gdb_test "python write_and_flush(\"some text\", style=None)" \ "^some text" "unstyled text, pass style as None" gdb_test "python write_and_flush(\"some text\", style=filename_style)" \ "^\033\\\[34;41;2;23;24;27msome text\033\\\[m" \ "styled output, pass style by keyword" gdb_test "python write_and_flush(\"some text\", gdb.STDOUT, filename_style)" \ "^\033\\\[34;41;2;23;24;27msome text\033\\\[m" \ "styled output, pass style by position" gdb_test "python write_and_flush(\"some text\", style='filename')" \ [multi_line \ "Python Exception : 'style' argument must be gdb\\.Style or None, not str\\." \ "Error occurred in Python: 'style' argument must be gdb\\.Style or None, not str\\."]