# This testcase is part of GDB, the GNU debugger.

# Copyright 2019-2022 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/>.

# Test the set/show commands framework.  The test uses the
# "maintenance test-settings set/show xxx" subcommands to exercise
# TAB-completion and setting processing.

load_lib completion-support.exp

standard_testfile .c

if {[build_executable "failed to prepare" $testfile $srcfile debug]} {
    return -1
}

clean_restart

if { ![readline_is_used] } {
    untested "no tab completion support without readline"
    return -1
}

# Test the show command SHOW_CMD.  EXPECTED_RE is the expected output.
# Also verifies that $_gdb_maint_setting_str produces an equivalent output,
# matching it with EXPECTED_RE.  EXPECTED_RE double quotes are escaped
# unless EXPECTED_RE_ESCAPED is true, indicating the quotes in EXPECTED_RE
# are already escaped.
# The value for the setting corresponding to SHOW_CMD is then reset
# to RESET_VALUE, then set again to the value given by $_gdb_maint_setting_str
# and $_gdb_maint_setting.  The default value of RESET_VALUE (0) should work for
# most settings.  Note that we do not check that RESET_VALUE differs from
# the expected value, as we assume different values will be verified.
# The setting value must still be the one in force before calling show_setting.
# In other words, this verifies that
#   maint set test-settings <some_setting> $_gdb_maint_setting_str(<some_setting>)
#   maint set test-settings <some_setting> $_gdb_maint_setting(<some_setting>)
# do not change the setting value.
# This procedure makes it easier to make the test
# name/message unique, since we test the "show" commands many times.
# EXPECTED_RE is made part of the test name.
proc show_setting {show_cmd expected_re {expected_re_escaped 0} {reset_value 0}} {
    global gdb_prompt

    with_test_prefix "$show_cmd $expected_re" {
	gdb_test "$show_cmd" $expected_re "show"

	# Remove the first two words (such as "maint show") to have the
	# setting name to use for $_gdb_maint_setting_str.
	regsub "\[^ \]+ +\[^ \]+ +\(.*\)" $show_cmd "\\1" maint_setting
	if {$expected_re_escaped} {
	    set escaped_expected_re $expected_re
	} else {
	    regsub -all "\"" $expected_re "\\\\\\\"" escaped_expected_re
	}
	set test "print \$_gdb_maint_setting_str"
	set setting_str_value "xxxYYYxxx"
	gdb_test_multiple "print \$_gdb_maint_setting_str(\"$maint_setting\")" $test {
	    -re " = \"\($escaped_expected_re\)\".*$gdb_prompt $" {
		set setting_str_value $expect_out(1,string)
		regsub -all "\\\\" $expect_out(1,string) "" setting_str_value
		pass $test
	    }
	}

	# Change the setting value to RESET_VALUE, set it back to setting_str_value
	# and check we still have the original value.
	gdb_test_no_output "maintenance set $maint_setting $reset_value" "str reset $reset_value"
	gdb_test_no_output "maintenance set $maint_setting $setting_str_value" "str set again"
	gdb_test "$show_cmd" $expected_re "str show after reset+set again"

	# Same test, but with value captured from $_gdb_maint_setting.
	set test "print \$_gdb_maint_setting"
	set setting_value "xxxYYYxxx"
	gdb_test_multiple "print \$_gdb_maint_setting(\"$maint_setting\")" $test {
	    -re " = \"\(.*\)\".*$gdb_prompt $" {
		set setting_value $expect_out(1,string)
		regsub -all "\\\\" $expect_out(1,string) "" setting_value
		pass $test
	    }
	    -re " = \(.*\)\r\n$gdb_prompt $" {
		set setting_value $expect_out(1,string)
		pass $test
	    }
	}

	gdb_test_no_output "maintenance set $maint_setting $reset_value" "reset $reset_value"
	gdb_test_no_output "maintenance set $maint_setting $setting_value" "set again"
	gdb_test "$show_cmd" $expected_re "show after reset+set again"
    }
}

# Verifies that $_gdb_setting (SETTING) gives a value whose ptype matches EXPECTED.
proc check_type {setting expected} {
    with_test_prefix "check_type $setting $expected" {
	gdb_test "print \$_gdb_maint_setting(\"$setting\")"
	gdb_test "ptype $" "$expected"

	# Currently, GDB ptype <internal function> always tells it is type int.
	# ptype should better report an error such as:
	#   "No type information for GDB functions"
	# Test 'type int', so as to make it fail if ptype is changed.
	gdb_test "ptype \$_gdb_maint_setting(\"$setting\")" \
            "type = int"
    }
}

# var_Xinteger tests.  VARIANT determines which command/variant to
# exercise.
proc test-integer {variant} {
    set set_cmd "maint set test-settings $variant"
    set show_cmd "maint show test-settings $variant"

    # A bogus value.
    gdb_test "$set_cmd bogus" \
	"No symbol table is loaded\\.  Use the \"file\" command\\."

    # Seemingly-valid but not quite valid value.
    gdb_test "$set_cmd 1a" \
	"Invalid number \"1a\"\\."

    # Valid value followed by garbage.
    gdb_test "$set_cmd 1 1" \
	"A syntax error in expression, near `1'\\."

    # Valid value followed by garbage.
    gdb_test "$set_cmd 1 x" \
	"A syntax error in expression, near `x'\\."

    if {$variant == "zuinteger-unlimited"} {
	# -1 means unlimited.  Other negative values are rejected.  -1
	# -is tested further below, along the "unlimited" tests.
	gdb_test "$set_cmd -2" "only -1 is allowed to set as unlimited"
	check_type "test-settings $variant" "type = int"
    } elseif {$variant == "uinteger" || $variant == "zuinteger"} {
	# Negative values are not accepted.
	gdb_test "$set_cmd -1" "integer -1 out of range"
	gdb_test "$set_cmd -2" "integer -2 out of range"
	check_type "test-settings $variant" "type = unsigned int"
    } else {
	# Negative values are not accepted.
	gdb_test_no_output "$set_cmd -1"
	show_setting "$show_cmd" "-1"
	gdb_test_no_output "$set_cmd -2"
	show_setting "$show_cmd" "-2"
	check_type "test-settings $variant" "type = int"
    }

    # Regular integer is accepted.
    gdb_test_no_output "$set_cmd 999"
    show_setting "$show_cmd" "999"

    if {$variant == "zinteger" || $variant == "zuinteger"} {
	# 0 means 0.
	gdb_test_no_output "$set_cmd 0"
	show_setting "$show_cmd" "0"
    } else {
	# Either 0 or -1 mean unlimited.  Test both the number and
	# "unlimited".  For the latter, test both full name and
	# abbreviations.

	if {$variant == "zuinteger-unlimited"} {
	    gdb_test_no_output "$set_cmd -1"
	} else {
	    gdb_test_no_output "$set_cmd 0"
	}
	show_setting "$show_cmd" "unlimited"

	foreach_with_prefix value {
	    "u"
	    "un"
	    "unl"
	    "unli"
	    "unlim"
	    "unlimi"
	    "unlimit"
	    "unlimite"
	    "unlimited"
	} {
	    # Alternate between integer and unlimited, to make sure the
	    # setting really took effect.
	    gdb_test_no_output "$set_cmd 1"
	    show_setting "$show_cmd" "1"

	    gdb_test_no_output "$set_cmd $value"
	    show_setting "$show_cmd" "unlimited"
	}
    }

    if {$variant == "zuinteger"} {
	test_gdb_complete_multiple "maint set test-settings " "zuinteger" "" {
	    "zuinteger"
	    "zuinteger-unlimited"
	}
    } else {
	test_gdb_complete_unique \
	    "$set_cmd" \
	    "$set_cmd"
    }

    if {$variant == "zinteger" || $variant == "zuinteger"} {
	test_gdb_complete_none \
	    "$set_cmd "
    } else {
	test_gdb_complete_unique \
	    "$set_cmd " \
	    "$set_cmd unlimited"
    }

    # Check junk after "unlimited".
    gdb_test "$set_cmd unlimitedu" "No symbol table is loaded.*"

    if {$variant == "zinteger" || $variant == "zuinteger"} {
	gdb_test "$set_cmd unlimited u" "No symbol table is loaded.*"
	gdb_test "$set_cmd unlimited 1" "No symbol table is loaded.*"
	gdb_test "$set_cmd unlimited -1" "No symbol table is loaded.*"
    } else {
	gdb_test "$set_cmd unlimited u" "Junk after \"unlimited\": u"
	gdb_test "$set_cmd unlimited 1" "Junk after \"unlimited\": 1"
	gdb_test "$set_cmd unlimited -1" "Junk after \"unlimited\": -1"
    }

    test_gdb_complete_none "$set_cmd unlimited "
    test_gdb_complete_none "$set_cmd unlimitedu"
    test_gdb_complete_none "$set_cmd unlimited u"
    test_gdb_complete_none "$set_cmd unlimited 1"
    test_gdb_complete_none "$set_cmd x"
    test_gdb_complete_none "$set_cmd x "
    test_gdb_complete_none "$set_cmd -1"
    test_gdb_complete_none "$set_cmd -1 "
    test_gdb_complete_none "$set_cmd 1 "

    # Check show command completion.
    if {$variant == "zuinteger"} {
	test_gdb_complete_multiple "maintenance show test-settings " "zuinteger" "" {
	    "zuinteger"
	    "zuinteger-unlimited"
	}
    } else {
	test_gdb_complete_unique \
	    "$show_cmd" \
	    "$show_cmd"
    }
    test_gdb_complete_none "$show_cmd "
}

# boolean tests.
proc_with_prefix test-boolean {} {
    # Use these variables to make sure we don't call the wrong command
    # by mistake.
    set set_cmd "maint set test-settings boolean"
    set show_cmd "maint show test-settings boolean"

    # A bogus value.
    gdb_test "$set_cmd bogus" \
	"\"on\" or \"off\" expected\\."

    # Seemingly-valid but not quite valid value.
    gdb_test "$set_cmd on1" \
	"\"on\" or \"off\" expected\\."

    # Valid value followed by garbage.
    gdb_test "$set_cmd on 1" \
	"\"on\" or \"off\" expected\\."

    # Unlike auto-bool settings, "-1" is not accepted.
    gdb_test "$set_cmd -1" \
	"\"on\" or \"off\" expected\\."

    # Nor "auto".
    gdb_test "$set_cmd auto" \
	"\"on\" or \"off\" expected\\."

    # "o" is ambiguous.
    gdb_test "$set_cmd o" \
	"\"on\" or \"off\" expected\\."

    # Various valid values.  Test both full value names and
    # abbreviations.

    # Note that unlike with auto-bool, empty value implies "on".
    foreach_with_prefix value {
	""
	"on"
	"1"
	"y"
	"ye"
	"yes"
	"e"
	"en"
	"ena"
	"enab"
	"enabl"
	"enable"
    } {
	gdb_test_no_output "$set_cmd off"
	show_setting "$show_cmd" "off"

	gdb_test_no_output "$set_cmd $value"
	show_setting "$show_cmd" "on"
    }

    check_type "test-settings boolean" "type = int"

    foreach_with_prefix value {
	"of"
	"off"
	"0"
	"n"
	"no"
	"d"
	"di"
	"dis"
	"disa"
	"disab"
	"disabl"
	"disable"
    } {
	gdb_test_no_output "$set_cmd on"
	show_setting "$show_cmd" "on"

	gdb_test_no_output "$set_cmd $value"
	show_setting "$show_cmd" "off"
    }

    test_gdb_complete_multiple "$set_cmd " "" "o" {
	"off"
	"on"
    }

    test_gdb_complete_unique \
	"$set_cmd of" \
	"$set_cmd off"

    test_gdb_complete_none "$set_cmd x"

    # Check that the show command doesn't complete anything.
    test_gdb_complete_unique \
	"$show_cmd" \
	"$show_cmd"
    test_gdb_complete_none "$show_cmd "
}

# auto-boolean tests.
proc_with_prefix test-auto-boolean {} {
    # Use these variables to make sure we don't call the wrong command
    # by mistake.
    set set_cmd "maint set test-settings auto-boolean"
    set show_cmd "maint show test-settings auto-boolean"

    # A bogus value.
    gdb_test "$set_cmd bogus" \
	"\"on\", \"off\" or \"auto\" expected\\."

    # Seemingly-valid but not quite valid value.
    gdb_test "$set_cmd on1" \
	"\"on\", \"off\" or \"auto\" expected\\."

    # Valid value followed by garbage.
    gdb_test "$set_cmd on 1" \
	"\"on\", \"off\" or \"auto\" expected\\."

    # "o" is ambiguous.
    gdb_test "$set_cmd o" \
	"\"on\", \"off\" or \"auto\" expected\\."

    # Various valid values.  Test both full value names and
    # abbreviations.

    foreach_with_prefix value {
	"on"
	"1"
	"y"
	"ye"
	"yes"
	"e"
	"en"
	"ena"
	"enab"
	"enabl"
	"enable"
    } {
	gdb_test_no_output "$set_cmd off"
	show_setting "$show_cmd" "off"

	gdb_test_no_output "$set_cmd $value"
	show_setting "$show_cmd" "on"
    }

    foreach_with_prefix value {
	"of"
	"off"
	"0"
	"n"
	"no"
	"d"
	"di"
	"dis"
	"disa"
	"disab"
	"disabl"
	"disable"
    } {
	gdb_test_no_output "$set_cmd on"
	show_setting "$show_cmd" "on"

	gdb_test_no_output "$set_cmd $value"
	show_setting "$show_cmd" "off"
    }

    foreach_with_prefix value {
	"a"
	"au"
	"aut"
	"auto"
	"-1"
    } {
	gdb_test_no_output "$set_cmd on"
	show_setting "$show_cmd" "on"

	gdb_test_no_output "$set_cmd $value"
	show_setting "$show_cmd" "auto"
    }

    check_type "test-settings auto-boolean" "type = int"

    # "-" is not accepted as abbreviation of "-1".
    gdb_test "$set_cmd -" \
	"\"on\", \"off\" or \"auto\" expected\\."

    test_gdb_complete_multiple "$set_cmd " "" "" {
	"auto"
	"off"
	"on"
    }

    test_gdb_complete_unique \
	"$set_cmd of" \
	"$set_cmd off"

    test_gdb_complete_none "$set_cmd x"

    # Check that the show command doesn't complete anything.
    test_gdb_complete_unique \
	"$show_cmd" \
	"$show_cmd"
    test_gdb_complete_none "$show_cmd "
}

# Enum option tests.
proc_with_prefix test-enum {} {
    # Use these variables to make sure we don't call the wrong command
    # by mistake.
    set set_cmd "maint set test-settings enum"
    set show_cmd "maint show test-settings enum"

    # Missing value.
    gdb_test "$set_cmd" \
	"Requires an argument\\. Valid arguments are xxx, yyy, zzz\\."

    # A bogus value.
    gdb_test "$set_cmd bogus" \
	"Undefined item: \"bogus\"."

    # Seemingly-valid but not quite valid value.
    gdb_test "$set_cmd xxx1" \
	"Undefined item: \"xxx1\"."

    # Valid value followed by garbage.
    gdb_test "$set_cmd xxx 1" \
	"Junk after item \"xxx\": 1"
    # Valid value followed by garbage, with extra spaces.
    gdb_test "$set_cmd xxx      1" \
	"Junk after item \"xxx\": 1"
    # Abbreviated value followed by garbage.
    gdb_test "$set_cmd xx 1" \
	"Junk after item \"xx\": 1"

    # Various valid values.  Test both full value names and
    # abbreviations.
    gdb_test_no_output "$set_cmd x"
    show_setting "$show_cmd" "xxx" 0 "zzz"
    gdb_test_no_output "$set_cmd yy"
    show_setting "$show_cmd" "yyy" 0 "zzz"
    gdb_test_no_output "$set_cmd zzz"
    show_setting "$show_cmd" "zzz" 0 "yyy"

    check_type "test-settings enum" "type = char \\\[3\\\]"

    test_gdb_complete_multiple "$set_cmd " "" "" {
	"xxx"
	"yyy"
	"zzz"
    }

    test_gdb_complete_unique \
	"$set_cmd x" \
	"$set_cmd xxx"

    test_gdb_complete_none "$set_cmd a"

    # Check that the show command doesn't complete anything.
    test_gdb_complete_unique \
	"$show_cmd" \
	"$show_cmd"
    test_gdb_complete_none "$show_cmd "
}

# string settings tests.
proc test-string {variant} {
    global gdb_prompt
    global srcfile binfile

    # Load symbols for the completion test below.
    clean_restart $binfile

    # Use these variables to make sure we don't call the wrong command
    # by mistake.
    set set_cmd "maint set test-settings $variant"
    set show_cmd "maint show test-settings $variant"

    # Checks that gdb doesn't crash if we haven't set the string yet.
    if {$variant != "filename"} {
	gdb_test "$show_cmd" "^$show_cmd\r\n" "$show_cmd: show default"
    } else {
	gdb_test "$show_cmd" "/foo/bar" "$show_cmd: show default"
    }

    # A string value.
    gdb_test_no_output "$set_cmd hello world"
    show_setting "$show_cmd" "hello world"

    check_type "test-settings $variant" "type = char \\\[\[1-9\]\[0-9\]*\\\]"

    # A quoted string value.
    if {$variant == "string"} {
	gdb_test_no_output "$set_cmd \"hello world\""
	show_setting "$show_cmd" "\\\\\"hello world\\\\\"" 1
    } else {
	gdb_test_no_output "$set_cmd \"hello world\""
	show_setting "$show_cmd" "\"hello world\""
    }

    # Test clearing the string.
    with_test_prefix "clear string" {
	if {$variant == "filename"} {
	    gdb_test "$set_cmd" \
		"Argument required \\(filename to set it to\\.\\)\\."

	    # Check the value didn't change.
	    show_setting "$show_cmd" "\"hello world\""
	} else {
	    gdb_test_no_output "$set_cmd"
	    gdb_test "$show_cmd" \
		"^$show_cmd\r\n" "$show_cmd: empty second time"
	}
    }

    # Test completion.
    if {$variant == "string" || $variant == "string-noescape" } {
	# Make sure GDB doesn't try to complete on symbols, which
	# doesn't make any sense.
	test_gdb_complete_none "$set_cmd "
    } else {
	# Complete on filename.

	# See comments in gdb.base/completion.exp.

	# We `cd' to ${srcdir}, and then do the completion relative to
	# the current directory.

	# ${srcdir} may be a relative path.  We want to make sure we
	# end up in the right directory - so make sure we know where
	# it is.
	global srcdir
	set mydir [pwd]
	cd ${srcdir}
	set fullsrcdir [pwd]
	cd ${mydir}

	gdb_test "cd ${fullsrcdir}" \
	    "Working directory [string_to_regexp ${fullsrcdir}].*" \
	    "cd to \${srcdir}"

	set unique_file ../testsuite/gdb.base/comp-dir/subdir/dummy

	test_gdb_complete_unique \
	    "$set_cmd ${unique_file}" \
	    "$set_cmd ${unique_file}.txt"

	test_gdb_complete_none "$set_cmd ${unique_file}.abc"
    }

    # Check show command completion.
    if {$variant == "string"} {
	test_gdb_complete_multiple "maint show test-settings " "string" "" {
	    "string"
	    "string-noescape"
	}
    } else {
	test_gdb_complete_unique \
	    "$show_cmd" \
	    "$show_cmd"
    }
    test_gdb_complete_none "$show_cmd "
}

foreach variant {
    uinteger
    integer
    zinteger
    zuinteger
    zuinteger-unlimited
} {
    with_test_prefix "test-integer $variant" {
	test-integer $variant
    }
}

test-boolean
test-auto-boolean
test-enum

foreach variant {
    string
    string-noescape
    optional-filename
    filename
} {
    with_test_prefix "test-string $variant" {
	test-string $variant
    }
}