(testsuite) Split assert_complete() into

assert_complete_many() and assert_complete_one().
Fix ssh completion now that match_items() also matches on prompt.
This commit is contained in:
Freddy Vulto 2010-11-22 22:57:00 +01:00
parent c1d3cdad70
commit 5a38f828d4
4 changed files with 206 additions and 116 deletions

View File

@ -40,7 +40,7 @@ set test "Tab should complete CDPATH"
assert_bash_exec "declare -p CDPATH &>/dev/null && OLDCDPATH=\$CDPATH || :" assert_bash_exec "declare -p CDPATH &>/dev/null && OLDCDPATH=\$CDPATH || :"
assert_bash_exec "CDPATH=\$PWD"; assert_bash_exec "CDPATH=\$PWD";
assert_complete "$::srcdir/fixtures/shared/default/foo.d/" \ assert_complete "$::srcdir/fixtures/shared/default/foo.d/" \
"cd $::srcdir/fixtures/shared/default/fo" $test -nospace "cd $::srcdir/fixtures/shared/default/fo" $test -nospace -expect-cmd-minus fo
sync_after_int sync_after_int
# Reset CDPATH # Reset CDPATH
assert_bash_exec "declare -p OLDCDPATH &>/dev/null && CDPATH=\$OLDCDPATH || unset CDPATH && unset OLDCDPATH" assert_bash_exec "declare -p OLDCDPATH &>/dev/null && CDPATH=\$OLDCDPATH || unset CDPATH && unset OLDCDPATH"

View File

@ -54,7 +54,7 @@ set expected [list /test/path /test/path2 /second/path]
set cmd "mount mocksrv:/" set cmd "mount mocksrv:/"
assert_bash_exec {OLDPATH="$PATH"; PATH="$SRCDIRABS/fixtures/mount/bin:$PATH";} assert_bash_exec {OLDPATH="$PATH"; PATH="$SRCDIRABS/fixtures/mount/bin:$PATH";}
# This needs an explicit cword param or will output "unresolved". # This needs an explicit cword param or will output "unresolved".
assert_complete $expected $cmd $test "/@" 20 "/" assert_complete $expected $cmd $test -expect-cmd-minus /
sync_after_int sync_after_int
assert_bash_exec {PATH="$OLDPATH"; unset -v OLDPATH} assert_bash_exec {PATH="$OLDPATH"; unset -v OLDPATH}
@ -93,7 +93,7 @@ assert_complete {/mnt/nice\ test\\path} {mnt /mnt/nice\ test\\p}
sync_after_int sync_after_int
assert_complete {{/mnt/nice\ test\\path} {/mnt/nice\ test-path}} \ assert_complete {{/mnt/nice\ test\\path} {/mnt/nice\ test-path}} \
{mnt /mnt/nice\ } "" /@ 20 {/mnt/nice\ } {mnt /mnt/nice\ } "" -expect-cmd-minus {/mnt/nice\ }
sync_after_int sync_after_int
assert_complete {/mnt/nice\$test-path} {mnt /mnt/nice\$} assert_complete {/mnt/nice\$test-path} {mnt /mnt/nice\$}

View File

@ -29,7 +29,6 @@ expect {
-re /@ { unresolved "$test at prompt" } -re /@ { unresolved "$test at prompt" }
default { unresolved "$test" } default { unresolved "$test" }
} }
sync_after_int
sync_after_int sync_after_int
@ -61,15 +60,16 @@ sync_after_int
set test "First argument should complete partial hostname" set test "First argument should complete partial hostname"
assert_complete_partial [get_hosts] ssh "" $test \ assert_complete_partial [get_hosts] ssh "" $test -ltrim-colon-completions
-filters "ltrim_colon_completions"
sync_after_int sync_after_int
set test "-F should complete filename" set test "-F should complete filename"
assert_complete "-Fspaced\\ \\ conf" "ssh -Fsp" "-F should complete filename" assert_complete "-Fspaced\\ \\ conf" "ssh -Fsp" $test
sync_after_int sync_after_int

View File

@ -1,6 +1,6 @@
# Source `init.tcl' again to restore the `unknown' procedure # Source `init.tcl' again to restore the `unknown' procedure
# NOTE: DejaGnu has an old `unknown' procedure which unfortunately disables # NOTE: DejaGnu has an old `unknown' procedure which unfortunately disables
# tcl auto-loading. # tcl auto-loading.
source [file join [info library] init.tcl] source [file join [info library] init.tcl]
package require cmdline package require cmdline
package require textutil::string package require textutil::string
@ -133,7 +133,30 @@ proc assert_bash_list_dir {expected cmd dir test {args {}}} {
# Make sure the expected items are returned by TAB-completing the specified # Make sure the expected items are returned by TAB-completing the specified
# command. # command. If the number of expected items is one, expected is:
#
# $cmd<TAB>$expected[<SPACE>]
#
# SPACE is not expected if -nospace is specified.
#
# If the number of expected items is greater than one, expected is:
#
# $cmd<TAB>\n
# $expected\n
# $prompt + ($cmd - AUTO) + longest-common-prefix-of-$expected
#
# AUTO is calculated like this: If $cmd ends with non-whitespace, and
# the last argument of $cmd equals the longest-common-prefix of
# $expected, $cmd minus this argument will be expected.
#
# If the algorithm above fails, you can manually specify the CWORD to be
# subtracted from $cmd specifying `-expect-cmd-minus CWORD'. Known cases where
# this is useful are when:
# - the last whitespace is escaped, e.g. "finger foo\ " or "finger
# 'foo "
#
# If the entire $cmd is expected, specify `-expect-cmd-full'.
#
# @param list $expected Expected completions. # @param list $expected Expected completions.
# @param string $cmd Command given to generate items # @param string $cmd Command given to generate items
# @param string $test Test title # @param string $test Test title
@ -141,38 +164,110 @@ proc assert_bash_list_dir {expected cmd dir test {args {}}} {
# -prompt PROMPT Bash prompt. Default is `/@' # -prompt PROMPT Bash prompt. Default is `/@'
# -chunk-size CHUNK-SIZE Compare list CHUNK-SIZE items at # -chunk-size CHUNK-SIZE Compare list CHUNK-SIZE items at
# a time. Default is 20. # a time. Default is 20.
# -cword CWORD Last argument of $cmd which is an argument-to-complete and
# to be replaced with the longest common prefix of $expected. If empty
# string (default), `assert_complete' autodetects if the last argument
# is an argument-to-complete by checking if $cmd doesn't end with
# whitespace. Specifying `cword' should only be necessary if this
# autodetection fails, e.g. when the last whitespace is escaped or
# quoted, e.g. "finger foo\ " or "finger 'foo "
# -nospace Don't expect space character to be output after completion match. # -nospace Don't expect space character to be output after completion match.
# -filters List of filters to apply to this function to tweak the expected # Valid only if a single completion is expected.
# completions and argument-to-complete. Possible values: # -ltrim-colon-completions Left-trim completions with cword containing
# - "ltrim_colon_completions" # colon (:)
#proc assert_complete {expected cmd {test ""} {prompt /@} {size 20} {cword ""} {filters ""}} { # -expect-cmd-full Expect the full $cmd to be echoed. Expected is:
# @result boolean True if successful, False if not #
# $cmd<TAB>\n
# $expected\n
# $prompt + $cmd + longest-common-prefix-of-$expected
#
# -expect-cmd-minus DWORD Expect $cmd minus DWORD to be echoed.
# Expected is:
#
# $cmd<TAB>\n
# $expected\n
# $prompt + ($cmd - DWORD) + longest-common-prefix-of-$expected
#
proc assert_complete {expected cmd {test ""} {args {}}} { proc assert_complete {expected cmd {test ""} {args {}}} {
set args_orig $args
array set arg [::cmdline::getoptions args { array set arg [::cmdline::getoptions args {
{prompt.arg "/@" "bash prompt"} {prompt.arg "/@" "bash prompt"}
{chunk-size.arg 20 "compare N list items at a time"} {chunk-size.arg 20 "compare N list items at a time"}
{cword.arg "" "word to complete"}
{nospace "don't expect space after completion"} {nospace "don't expect space after completion"}
{filters.arg "" "filters to preprocess expected completions"} {ltrim-colon-completions "left-trim completions with cword containing :"}
{expect-cmd-full "Expect full cmd after prompt"}
{expect-cmd-minus.arg "" "Expect cmd minus DWORD after prompt"}
}] }]
set cword $arg(cword)
set prompt $arg(prompt)
if {[llength $expected] == 0} { if {[llength $expected] == 0} {
assert_no_complete $cmd $test assert_no_complete $cmd $test
} elseif {[llength $expected] == 1} {
eval assert_complete_one \$expected \$cmd \$test $args_orig
} else { } else {
if {$test == ""} {set test "$cmd should show completions"} eval assert_complete_many \$expected \$cmd \$test $args_orig
send "$cmd\t" }
if {[llength $expected] == 1} { }
expect -ex "$cmd"
if {[lsearch -exact $arg(filters) "ltrim_colon_completions"] == -1} {
# Make sure the expected multiple items are returned by TAB-completing the
# specified command.
# @see assert_complete()
proc assert_complete_many {expected cmd {test ""} {args {}}} {
array set arg [::cmdline::getoptions args {
{prompt.arg "/@" "bash prompt"}
{chunk-size.arg 20 "compare N list items at a time"}
{nospace "don't expect space after completion"}
{ltrim-colon-completions "left-trim completions with cword containing :"}
{expect-cmd-full "Expect full cmd after prompt"}
{expect-cmd-minus.arg "" "Expect cmd minus CWORD after prompt"}
}]
if {$test == ""} {set test "$cmd should show completions"}
set prompt $arg(prompt)
set dword ""
if {$arg(expect-cmd-minus) != ""} {set dword $arg(expect-cmd-minus)}
send "$cmd\t"
expect -ex "$cmd\r\n"
# Make sure expected items are unique
set expected [lsort -unique $expected]
# Determine common prefix of completions
set common [::textutil::string::longestCommonPrefixList $expected]
if {$arg(ltrim-colon-completions)} {
# If partial contains colon (:), remove partial from begin of items
_ltrim_colon_completions $cmd expected dword
}
set cmd2 [_remove_cword_from_cmd $cmd $dword $common]
set prompt "$prompt$cmd2$common"
if {$arg(nospace)} {set endspace ""} else {set endspace "-end-space"}
set endprompt "-end-prompt"
if {[
eval match_items \$expected -bash-sort -chunk-size \
\$arg(chunk-size) $endprompt $endspace -prompt \$prompt
]} {
pass "$test"
} else {
fail "$test"
}
}
# Make sure the expected single item is returned by TAB-completing the
# specified command.
# @see assert_complete()
proc assert_complete_one {expected cmd {test ""} {args {}}} {
array set arg [::cmdline::getoptions args {
{prompt.arg "/@" "bash prompt"}
{chunk-size.arg 20 "compare N list items at a time"}
{nospace "don't expect space after completion"}
{ltrim_colon_completions "left-trim completions with cword containing :"}
{expect-cmd-full "Expect full cmd after prompt"}
{expect-cmd-minus.arg "" "Expect cmd minus CWORD after prompt"}
}]
set prompt $arg(prompt)
if {$test == ""} {set test "$cmd should show completion"}
send "$cmd\t"
expect -ex "$cmd"
if {$arg(ltrim_colon_completions)} {
# If partial contains colon (:), remove partial from begin of items
_ltrim_colon_completions cword expected
} else {
set cur ""; # Default to empty word to complete on set cur ""; # Default to empty word to complete on
set words [split_words_bash $cmd] set words [split_words_bash $cmd]
if {[llength $words] > 1} { if {[llength $words] > 1} {
@ -180,88 +275,61 @@ proc assert_complete {expected cmd {test ""} {args {}}} {
set index [expr [llength $words] - 1] set index [expr [llength $words] - 1]
set cur [lindex $words $index] set cur [lindex $words $index]
} }
# Remove second word from beginning of single item $expected # Remove second word from beginning of $expected
if {[string first $cur $expected] == 0} { if {[string first $cur $expected] == 0} {
set expected [list [string range $expected [string length $cur] end]] set expected [list [string range $expected [string length $cur] end]]
} }
} }
} else {
expect -ex "$cmd\r\n"
# Make sure expected items are unique
set expected [lsort -unique $expected]
}
if {[lsearch -exact $arg(filters) "ltrim_colon_completions"] != -1} {
# If partial contains colon (:), remove partial from begin of items
# See also: bash_completion.__ltrim_colon_completions()
_ltrim_colon_completions cword expected
}
if {$arg(nospace)} {set endspace ""} else {set endspace "-end-space"} if {$arg(nospace)} {set endspace ""} else {set endspace "-end-space"}
if {[ if {[
eval match_items \$expected -bash-sort -chunk-size \ eval match_items \$expected -bash-sort -chunk-size \
\$arg(chunk-size) $endspace -prompt \$prompt \$arg(chunk-size) $endspace -prompt \$prompt
]} { ]} {
if {[llength $expected] == 1} {
pass "$test" pass "$test"
} else {
# Remove optional (partial) last argument-to-complete from `cmd',
# E.g. "finger test@" becomes "finger"
if {[lsearch -exact $arg(filters) "ltrim_colon_completions"] != -1} {
set cmd2 $cmd
} else {
set cmd2 [_remove_cword_from_cmd $cmd $cword]
}
# Determine common prefix of completions
set common [::textutil::string::longestCommonPrefixList $expected]
#if {[string length $common] > 0} {set common " $common"}
expect {
-ex "$prompt$cmd2$common" { pass "$test" }
-re $prompt { unresolved "$test at prompt" }
-re eof { unresolved "eof" }
}
}
} else { } else {
fail "$test" fail "$test"
} }
}
} }
# @param string $cmd Command to remove cword from # @param string $cmd Command to remove current-word-to-complete from.
# @param string $cword (optional) Last argument of $cmd which is an # @param string $dword (optional) Manually specify current-word-to-complete,
# argument-to-complete and to be deleted. If empty string (default), # i.e. word to remove from $cmd. If empty string (default),
# `_remove_cword_from_cmd' autodetects if the last argument is an # `_remove_cword_from_cmd' autodetects if the last argument is the
# argument-to-complete by checking if $cmd doesn't end with whitespace. # current-word-to-complete by checking if $cmd doesn't end with whitespace.
# Specifying `cword' is only necessary if this autodetection fails, e.g. # Specifying `dword' is only necessary if this autodetection fails, e.g.
# when the last whitespace is escaped or quoted, e.g. "finger foo\ " or # when the last whitespace is escaped or quoted, e.g. "finger foo\ " or
# "finger 'foo " # "finger 'foo "
# @return string Command with cword removed # @param string $common (optional) Common prefix of expected completions.
proc _remove_cword_from_cmd {cmd {cword ""}} { # @return string Command with current-word-to-complete removed
proc _remove_cword_from_cmd {cmd {dword ""} {common ""}} {
set cmd2 $cmd set cmd2 $cmd
# Is $cword specified? # Is $dword specified?
if {[string length $cword] > 0} { if {[string length $dword] > 0} {
# Remove $cword from end of $cmd # Remove $dword from end of $cmd
if {[string last $cword $cmd] == [string length $cmd] - [string length $cword]} { if {[string last $dword $cmd] == [string length $cmd] - [string length $dword]} {
set cmd2 [string range $cmd 0 [expr [string last $cword $cmd] - 1]] set cmd2 [string range $cmd 0 [expr [string last $dword $cmd] - 1]]
} }
} else { } else {
# No, $cword not specified; # No, $dword not specified;
# Check if last argument is really an-argument-to-complete, i.e. # Check if last argument is really a word-to-complete, i.e.
# doesn't end with whitespace. # doesn't end with whitespace.
# NOTE: This check fails if trailing whitespace is escaped or quoted, # NOTE: This check fails if trailing whitespace is escaped or quoted,
# e.g. "finger foo\ " or "finger 'foo ". Specify parameter # e.g. "finger foo\ " or "finger 'foo ". Specify parameter
# $cword in those cases. # $dword in those cases.
# Is last char whitespace? # Is last char whitespace?
if {! [string is space [string range $cmd end end]]} { if {! [string is space [string range $cmd end end]]} {
# No, last char isn't whitespace; # No, last char isn't whitespace;
# Remove argument-to-complete from end of $cmd set cmds [split $cmd]
set cmd2 [lrange [split $cmd] 0 end-1] # Does word-to-complete start with $common?
if {[string first $common [lrange $cmds end end]] == 0} {
# Remove word-to-complete from end of $cmd
set cmd2 [lrange $cmds 0 end-1]
append cmd2 " " append cmd2 " "
} }
} }
}
return $cmd2 return $cmd2
} }
@ -342,32 +410,47 @@ proc assert_complete_partial {expected cmd {partial ""} {test ""} {args {}}} {
lappend pick $item lappend pick $item
} }
} }
assert_complete $pick "$cmd $partial" $test $args # NOTE: The `eval' is necessary to flatten the $args list
# See also: http://wiki.tcl.tk/11787 - {expand}
eval assert_complete \$pick \"\$cmd \$partial\" \$test $args; #"
} }
} }
# If cword contains colon (:), left-trim completions with cword
# @param string $cmd Command to complete
# @param list $items Reference to list of completions to trim
# @param string $dword Reference to variable to contain word to remove from
# expected cmd.
# See also: bash_completion._ltrim_colon_completions # See also: bash_completion._ltrim_colon_completions
proc _ltrim_colon_completions {cword items} { proc _ltrim_colon_completions {cmd items dword} {
upvar 1 $cword cword_out
upvar 1 $items items_out upvar 1 $items items_out
upvar 1 $dword dword_out
set cur ""; # Default to empty word to complete on
set words [split_words_bash $cmd]
if {[llength $words] > 1} {
# Assume last word of `$cmd' is word to complete on.
set index [expr [llength $words] - 1]
set cur [lindex $words $index]
}
# If word-to-complete contains a colon, # If word-to-complete contains a colon,
# and bash-version < 4, # and bash-version < 4,
# or bash-version >= 4 and COMP_WORDBREAKS contains a colon # or bash-version >= 4 and COMP_WORDBREAKS contains a colon
if { if {
[string first : $cword_out] > -1 && ( [string first : $cur] > -1 && (
[lindex $::BASH_VERSINFO 0] < 4 || [lindex $::BASH_VERSINFO 0] < 4 ||
([lindex $::BASH_VERSINFO 0] >= 4 && [string first ":" $::COMP_WORDBREAKS] > -1) ([lindex $::BASH_VERSINFO 0] >= 4 && [string first ":" $::COMP_WORDBREAKS] > -1)
) )
} { } {
set dword_out $cur
for {set i 0} {$i < [llength $items_out]} {incr i} { for {set i 0} {$i < [llength $items_out]} {incr i} {
set item [lindex $items_out $i] set item [lindex $items_out $i]
if {[string first $cword_out $item] == 0} { if {[string first $cur $item] == 0} {
# Strip colon-prefix # Strip colon-prefix
lset items_out $i [string range $item [string length $cword_out] end] lset items_out $i [string range $item [string length $cur] end]
} }
} }
#set cword_out ""
} }
} }
@ -694,8 +777,15 @@ proc match_items {items {args {}}} {
timeout { set result false; break } timeout { set result false; break }
} }
} else { } else {
set end ""
if {$arg(end-prompt) && $i + $j == [llength $items]} {
set end "$prompt"
_escape_regexp_chars end
# \$ matches real end of expect_out buffer
set end "$end\$"
}
expect { expect {
-re "^$expected" { set result true } -re "^$expected$end" { set result true }
default { set result false; break } default { set result false; break }
timeout { set result false; break } timeout { set result false; break }
} }