summaryrefslogtreecommitdiff
path: root/gdb/testsuite/lib/completion-support.exp
diff options
context:
space:
mode:
authorPedro Alves <palves@redhat.com>2017-11-24 23:41:12 +0000
committerPedro Alves <palves@redhat.com>2017-11-24 23:41:12 +0000
commit8955eb2da31d78690c762748fab3a16832ef1f81 (patch)
treee03fa77940af9eba7e83e8111eb960c599a8d017 /gdb/testsuite/lib/completion-support.exp
parent0662b6a7c1b3b04a4ca31a09af703c91c7aa9646 (diff)
downloadbinutils-gdb-8955eb2da31d78690c762748fab3a16832ef1f81.tar.gz
Comprehensive C++ linespec/completer tests
Exercises all sorts of aspects fixed by previous patches, going back a few months. - Exercises label completion, linespecs and explicit locations. - Exercises both quoting vs non-quoting, source filenames, function names, labels, with both linespecs and explicit locations. - Tests corner cases around not-quoting function names, and whitespace and/and completing inside a parameter or template argument list, anonymous namespace awareness, etc. E.g., "break foo<[TAB]" -> "break foo<int>()" "break bar ( int[TAB]" -> "break bar ( int) "break ( anon" -> "break ( anonymous namespace)::func()" "b cfunc() [tab]" -> "b cfunc() const" "b rettype templfunc[tab]" -> "b rettype templfunc<bar>()" ... and others. - Tests the "b source.c[TAB] -> b source.cc:" feature. I.e., colon auto-appending. - Exercises corner cases around C++ "operator<" / "operator<<". (Much more extensive C++ operator completion/linespec handling in a separate patch.) - Exercises both tab completion and "complete" command completion, using routines that handle it automatically, to ensure no test forgets either mode. - Many of the completion tests test completion at at prefix of a given tricky name, to make sure all corner cases are covered. E.g., completing before, at and after ":", "(", "<". - Exercises "keyword" completion. I.e., "b function() [TAB]" displaying "if task thread" as completion match list. Likewise for display explicit location options matches at the appropriate points. - Ensures that the completer finds the same breakpoint locations that setting a breakpoint finds. - Tests that linespec/location completion doesn't find data symbols. - Tests that expression completion still kicks in after a linespec/location keyword. I.e., this: "b function () if global1 + global[TAB]" knows that after "if", you're completing on an expression, and thus breaks words after "if" as an expression and matches on "global" as a data symbol. - Adds common routines to help with all the above, to be used by multiple completion and linespec/location test cases. - More... Grows the gdb.linespec/ tests like this: -# of expected passes 573 +# of expected passes 3464 gdb/testsuite/ChangeLog: 2017-11-24 Pedro Alves <palves@redhat.com> * gdb.linespec/cpcompletion.exp: New file. * gdb.linespec/cpls-hyphen.cc: New file. * gdb.linespec/cpls.cc: New file. * gdb.linespec/cpls2.cc: New file. * gdb.linespec/explicit.exp: Load completion-support.exp. Adjust test to use test_gdb_complete_unique. Add label completion, keyword completion and explicit location completion tests. * lib/completion-support.exp: New file.
Diffstat (limited to 'gdb/testsuite/lib/completion-support.exp')
-rw-r--r--gdb/testsuite/lib/completion-support.exp471
1 files changed, 471 insertions, 0 deletions
diff --git a/gdb/testsuite/lib/completion-support.exp b/gdb/testsuite/lib/completion-support.exp
new file mode 100644
index 00000000000..70e6aec895b
--- /dev/null
+++ b/gdb/testsuite/lib/completion-support.exp
@@ -0,0 +1,471 @@
+# Copyright 2017 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.
+
+# Any variable or procedure in the namespace whose name starts with
+# "_" is private to the module. Do not use these.
+
+namespace eval completion {
+ variable bell_re "\\\x07"
+
+ # List of all quote chars.
+ variable all_quotes_list {"'" "\""}
+
+ # List of all quote chars, including no-quote at all.
+ variable maybe_quoted_list {"" "'" "\""}
+
+ variable keyword_list {"if" "task" "thread"}
+
+ variable explicit_opts_list \
+ {"-function" "-label" "-line" "-source"}
+}
+
+# Make a regular expression that matches a TAB completion list.
+
+proc make_tab_completion_list_re { completion_list } {
+ # readline separates the completion columns that fit on the same
+ # line with whitespace. Since we're testing under "set width
+ # unlimited", all completions will be printed on the same line.
+ # The amount of whitespace depends on the length of the widest
+ # completion. We could compute that here and expect the exact
+ # number of ws characters between each completion match, but to
+ # keep it simple, we accept any number of characters.
+ set ws " +"
+
+ set completion_list_re ""
+ foreach c $completion_list {
+ append completion_list_re [string_to_regexp $c]
+ append completion_list_re $ws
+ }
+ append completion_list_re $ws
+
+ return $completion_list_re
+}
+
+# Make a regular expression that matches a "complete" command
+# completion list. CMD_PREFIX is the command prefix added to each
+# completion match.
+
+proc make_cmd_completion_list_re { cmd_prefix completion_list start_quote_char end_quote_char } {
+
+ set completion_list_re ""
+ foreach c $completion_list {
+ # The command prefix is included in all completion matches.
+ append completion_list_re [string_to_regexp $cmd_prefix$start_quote_char$c$end_quote_char]
+ append completion_list_re "\r\n"
+ }
+
+ return $completion_list_re
+}
+
+# Clear the input line.
+
+proc clear_input_line { test } {
+ global gdb_prompt
+
+ send_gdb "\003"
+ gdb_test_multiple "" "$test (clearing input line)" {
+ -re "Quit\r\n$gdb_prompt $" {
+ }
+ }
+}
+
+# Test that completing LINE with TAB completes to nothing.
+
+proc test_gdb_complete_tab_none { line } {
+ set line_re [string_to_regexp $line]
+
+ set test "tab complete \"$line\""
+ send_gdb "$line\t"
+ gdb_test_multiple "" "$test" {
+ -re "^$line_re$completion::bell_re$" {
+ pass "$test"
+ }
+ }
+
+ clear_input_line $test
+}
+
+# Test that completing INPUT_LINE with TAB completes to
+# COMPLETE_LINE_RE. APPEND_CHAR_RE is the character expected to be
+# appended after EXPECTED_OUTPUT. Normally that's a whitespace, but
+# in some cases it's some other character, like a colon.
+
+proc test_gdb_complete_tab_unique { input_line complete_line_re append_char_re } {
+
+ set test "tab complete \"$input_line\""
+ send_gdb "$input_line\t"
+ gdb_test_multiple "" "$test" {
+ -re "^$complete_line_re$append_char_re$" {
+ pass "$test"
+ }
+ }
+
+ clear_input_line $test
+}
+
+# Test that completing INPUT_LINE with TAB completes to "INPUT_LINE +
+# ADD_COMPLETED_LINE" and that it displays the completion matches in
+# COMPLETION_LIST.
+
+proc test_gdb_complete_tab_multiple { input_line add_completed_line \
+ completion_list } {
+ global gdb_prompt
+
+ set input_line_re [string_to_regexp $input_line]
+ set add_completed_line_re [string_to_regexp $add_completed_line]
+
+ set expected_re [make_tab_completion_list_re $completion_list]
+
+ set test "tab complete \"$input_line\""
+ send_gdb "$input_line\t"
+ gdb_test_multiple "" "$test (first tab)" {
+ -re "^${input_line_re}${completion::bell_re}$add_completed_line_re$" {
+ send_gdb "\t"
+ # If we auto-completed to an ambiguous prefix, we need an
+ # extra tab to show the matches list.
+ if {$add_completed_line != ""} {
+ send_gdb "\t"
+ }
+ gdb_test_multiple "" "$test (second tab)" {
+ -re "$expected_re\r\n$gdb_prompt $input_line_re$add_completed_line_re$" {
+ pass "$test"
+ }
+ }
+ }
+ }
+
+ clear_input_line $test
+}
+
+# Test that completing LINE with the complete command completes to
+# nothing.
+
+proc test_gdb_complete_cmd_none { line } {
+ gdb_test_no_output "complete $line" "cmd complete \"$line\""
+}
+
+# Test that completing LINE with the complete command completes to
+# COMPLETE_LINE_RE.
+
+proc test_gdb_complete_cmd_unique { input_line complete_line_re } {
+ global gdb_prompt
+
+ set cmd "complete $input_line"
+ set cmd_re [string_to_regexp $cmd]
+ set test "cmd complete \"$input_line\""
+ gdb_test_multiple $cmd $test {
+ -re "^$cmd_re\r\n$complete_line_re\r\n$gdb_prompt $" {
+ pass $test
+ }
+ }
+}
+
+# Test that completing "CMD_PREFIX + COMPLETION_WORD" with the
+# complete command displays the COMPLETION_LIST completion list. Each
+# entry in the list should be prefixed by CMD_PREFIX.
+
+proc test_gdb_complete_cmd_multiple { cmd_prefix completion_word completion_list start_quote_char end_quote_char } {
+ global gdb_prompt
+
+ set expected_re [make_cmd_completion_list_re $cmd_prefix $completion_list $start_quote_char $end_quote_char]
+ set cmd_re [string_to_regexp "complete $cmd_prefix$completion_word"]
+ set test "cmd complete \"$cmd_prefix$completion_word\""
+ gdb_test_multiple "complete $cmd_prefix$completion_word" $test {
+ -re "^$cmd_re\r\n$expected_re$gdb_prompt $" {
+ pass $test
+ }
+ }
+}
+
+# Test that completing LINE completes to nothing.
+
+proc test_gdb_complete_none { input_line } {
+ test_gdb_complete_tab_none $input_line
+ test_gdb_complete_cmd_none $input_line
+}
+
+# Test that completing INPUT_LINE completes to COMPLETE_LINE_RE.
+#
+# APPEND_CHAR is the character expected to be appended after
+# EXPECTED_OUTPUT when TAB completing. Normally that's a whitespace,
+# but in some cases it's some other character, like a colon.
+#
+# If MAX_COMPLETIONS is true, then we expect the completion to hit the
+# max-completions limit. Since we're expecting a unique completion
+# match, this will only be visible in the "complete" command output.
+# Tab completion will just auto-complete the only match and won't
+# display a match list.
+#
+# Note: usually it's more convenient to pass a literal string instead
+# of a regular expression (as COMPLETE_LINE_RE). See
+# test_gdb_complete_unique below.
+
+proc test_gdb_complete_unique_re { input_line complete_line_re {append_char " "} {max_completions 0}} {
+ set append_char_re [string_to_regexp $append_char]
+ test_gdb_complete_tab_unique $input_line $complete_line_re $append_char_re
+
+ # Trim INPUT_LINE and COMPLETE LINE, for the case we're completing
+ # a command with leading whitespace. Leading command whitespace
+ # is discarded by GDB.
+ set input_line [string trimleft $input_line]
+ set expected_output_re [string trimleft $complete_line_re]
+ if {$append_char_re != " "} {
+ append expected_output_re $append_char_re
+ }
+ if {$max_completions} {
+ set max_completion_reached_msg \
+ "*** List may be truncated, max-completions reached. ***"
+ set input_line_re \
+ [string_to_regexp $input_line]
+ set max_completion_reached_msg_re \
+ [string_to_regexp $max_completion_reached_msg]
+
+ append expected_output_re \
+ "\r\n$input_line_re $max_completion_reached_msg_re"
+ }
+
+ test_gdb_complete_cmd_unique $input_line $expected_output_re
+}
+
+# Like TEST_GDB_COMPLETE_UNIQUE_RE, but COMPLETE_LINE is a string, not
+# a regular expression.
+
+proc test_gdb_complete_unique { input_line complete_line {append_char " "} {max_completions 0}} {
+ set complete_line_re [string_to_regexp $complete_line]
+ test_gdb_complete_unique_re $input_line $complete_line_re $append_char $max_completions
+}
+
+# Test that completing "CMD_PREFIX + COMPLETION_WORD" adds
+# ADD_COMPLETED_LINE to the input line, and that it displays
+# COMPLETION_LIST as completion match list. COMPLETION_WORD is the
+# completion word.
+
+proc test_gdb_complete_multiple { cmd_prefix completion_word add_completed_line completion_list {start_quote_char ""} {end_quote_char ""}} {
+ test_gdb_complete_tab_multiple "$cmd_prefix$completion_word" $add_completed_line $completion_list
+ test_gdb_complete_cmd_multiple $cmd_prefix $completion_word $completion_list $start_quote_char $end_quote_char
+}
+
+# Test that all the substring prefixes of COMPLETION from [0..START)
+# to [0..END) complete to COMPLETION. If END is ommitted, default to
+# the length of COMPLETION.
+
+proc test_complete_prefix_range {completion start {end -1}} {
+ if {$end == -1} {
+ set end [string length $completion]
+ }
+
+ for {set i $start} {$i < $end} {incr i} {
+ set line [string range $completion 0 $i]
+ test_gdb_complete_unique "$line" "$completion"
+ }
+}
+
+# Find NEEDLE in HAYSTACK and return the index _after_ NEEDLE. E.g.,
+# searching for "(" in "foo(int)" returns 4, which would be useful if
+# you want to find the "(" to try completing "foo(".
+
+proc index_after {needle haystack} {
+ set start [string first $needle $haystack]
+ if {$start == -1} {
+ error "could not find \"$needle\" in \"$haystack\""
+ }
+ return [expr $start + [string length $needle]]
+}
+
+# Create a breakpoint using BREAK_COMMAND, and return the number
+# of locations found.
+
+proc completion::_create_bp {break_command} {
+ global gdb_prompt
+ global decimal hex
+
+ set found_locations -1
+
+ set test "set breakpoint"
+ gdb_test_multiple "$break_command" $test {
+ -re "\\\(\($decimal\) locations\\\)\r\n$gdb_prompt $" {
+ set found_locations "$expect_out(1,string)"
+ }
+ -re "Breakpoint $decimal at $hex: file .*, line .*$gdb_prompt $" {
+ set found_locations 1
+ }
+ -re "Make breakpoint pending on future shared library load.*y or .n.. $" {
+ send_gdb "n\n"
+ gdb_test_multiple "" "$test (prompt)" {
+ -re "$gdb_prompt $" {
+ }
+ }
+ set found_locations 0
+ }
+ -re "invalid explicit location argument, \[^\r\n\]*\r\n$gdb_prompt $" {
+ set found_locations 0
+ }
+ -re "Function \[^\r\n\]* not defined in \[^\r\n\]*\r\n$gdb_prompt $" {
+ set found_locations 0
+ }
+ }
+ return $found_locations
+}
+
+# Return true if lists A and B have the same elements. Order of
+# elements does not matter.
+
+proc completion::_leq {a b} {
+ return [expr {[lsort $a] eq [lsort $b]}]
+}
+
+# Check that trying to create a breakpoint using BREAK_COMMAND fails.
+
+proc check_setting_bp_fails {break_command} {
+ with_test_prefix "\"$break_command\" creates no bp locations" {
+ set found_locations [completion::_create_bp $break_command]
+ gdb_assert {$found_locations == 0} "matches"
+ if {$found_locations != 0} {
+ delete_breakpoints
+ }
+ }
+}
+
+# Check that creating the breakpoint using BREAK_COMMAND finds the
+# same breakpoint locations as completing BREAK_COMMAND.
+# COMPLETION_LIST is the expected completion match list.
+
+proc check_bp_locations_match_list {break_command completion_list} {
+ global gdb_prompt
+ global hex
+
+ with_test_prefix "compare \"$break_command\" completion list with bp location list" {
+ set num_locations [completion::_create_bp $break_command]
+
+ set found_list ""
+
+ set any "\[^\r\n\]*"
+
+ gdb_test_multiple "info breakpoint \$bpnum" "info breakpoint" {
+ -re "in \(\[^\r\n\]*\) at " {
+ # A function location.
+ set found_location "$expect_out(1,string)"
+ lappend found_list $found_location
+ exp_continue
+ }
+ -re "breakpoint${any}keep${any}y${any}$hex\[ \t]*\(${any}\)\r\n" {
+ # A label location.
+ set found_location "$expect_out(1,string)"
+ lappend found_list $found_location
+ exp_continue
+ }
+ -re "$gdb_prompt $" {
+ }
+ }
+
+ gdb_assert {[completion::_leq $found_list $completion_list]} "matches"
+
+ delete_breakpoints
+ }
+}
+
+# Build linespec and explicit locations out of all the combinations of
+# SOURCES, FUNCTIONS and LABELS, with all combinations of possible
+# quoting and whitespace around separators, and run BODY_LINESPEC and
+# BODY_EXPLICIT in the context of the caller for each combination. A
+# variable named "location" is set in the callers context with the
+# currently iterated location.
+
+proc foreach_location_functions { sources functions body_linespec body_explicit } {
+ upvar source source
+ upvar function function
+ upvar source_sep source_sep
+ upvar location location
+
+ foreach source $sources {
+ # Test with and without source quoting.
+ foreach sqc $completion::maybe_quoted_list {
+ if {$source == "" && $sqc != ""} {
+ # Invalid combination.
+ continue
+ }
+
+ # Test with and without function quoting.
+ foreach fqc $completion::maybe_quoted_list {
+ # Test known and unknown functions.
+ foreach function $functions {
+ # Linespec version. Test with and without spacing
+ # after the source/colon colon separator.
+ foreach source_sep {"" ":" ": "} {
+ # Skip invalid combinations.
+ if {$source == "" && $source_sep != ""} {
+ continue
+ }
+ if {$source != "" && $source_sep == ""} {
+ continue
+ }
+
+ set location "${sqc}${source}${sqc}${source_sep}${fqc}$function${fqc}"
+ uplevel 1 $body_linespec
+ }
+
+ # Explicit locations version.
+ if {$source != ""} {
+ set loc_src "-source ${sqc}${source}${sqc} "
+ } else {
+ set loc_src ""
+ }
+
+ set location "${loc_src}-function ${fqc}$function${fqc}"
+ uplevel 1 $body_explicit
+ }
+ }
+ }
+ }
+}
+
+# Same as foreach_locations_functions, but also iterate over
+# combinations of labels.
+proc foreach_location_labels { sources functions labels body_linespec body_explicit } {
+ upvar source source
+ upvar function function
+ upvar label label
+ upvar source_sep source_sep
+ upvar label_sep label_sep
+ upvar location location
+
+ # Test both with a known source file and without a source file
+ # component.
+ foreach_location_functions \
+ $sources \
+ $functions \
+ {
+ # Linespec version. Test various spacing around the label
+ # colon separator.
+ set saved_location ${location}
+ foreach label_sep {":" " :" ": " " : "} {
+ # Test both known and unknown label.
+ foreach label $labels {
+ set location "${saved_location}${label_sep}$label"
+ uplevel 1 $body_linespec
+ }
+ }
+ } \
+ {
+ # Explicit locations version.
+ set saved_location ${location}
+ foreach label $labels {
+ set location "${saved_location} -label $label"
+ uplevel 1 $body_explicit
+ }
+ }
+}