summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES1
-rw-r--r--bash_completion138
-rw-r--r--doc/styleguide.txt23
-rw-r--r--doc/testing.txt49
-rw-r--r--test/Makefile.am1
-rw-r--r--test/completion/perldoc.exp4
-rw-r--r--test/fixtures/lftp/.lftp/bookmarks1
-rw-r--r--test/fixtures/shared/default/bar (renamed from test/fixture1/bar)0
-rw-r--r--test/fixtures/shared/default/bar bar.d/foo (renamed from test/fixture1/bar bar.d/foo)0
-rw-r--r--test/fixtures/shared/default/foo (renamed from test/fixture1/foo)0
-rw-r--r--test/fixtures/shared/default/foo.d/foo (renamed from test/fixture1/foo.d/foo)0
-rwxr-xr-xtest/generate4
-rw-r--r--test/lib/completions/acroread.exp6
-rw-r--r--test/lib/completions/cancel.exp2
-rw-r--r--test/lib/completions/cd.exp10
-rw-r--r--test/lib/completions/chown.exp40
-rw-r--r--test/lib/completions/find.exp2
-rw-r--r--test/lib/completions/lftp.exp6
-rw-r--r--test/lib/completions/perl.exp10
-rw-r--r--test/lib/completions/sbcl-mt.exp2
-rw-r--r--test/lib/completions/sbcl.exp2
-rw-r--r--test/lib/completions/screen.exp2
-rw-r--r--test/lib/completions/ssh.exp21
-rw-r--r--test/lib/completions/sudo.exp2
-rw-r--r--test/lib/library.exp154
-rw-r--r--test/unit/_get_comp_words_by_ref.exp339
-rw-r--r--test/unit/find_unique_completion_pair.exp37
27 files changed, 771 insertions, 85 deletions
diff --git a/CHANGES b/CHANGES
index c6baded9..db5ef761 100644
--- a/CHANGES
+++ b/CHANGES
@@ -68,6 +68,7 @@ bash-completion (2.x)
* Improve ssh -o suboption completion (Alioth: #312122).
* Fix NFS mounts completion (Alioth: #312285).
* Fix completion of usernames (Alioth: #311396, Debian: #511788).
+ * Fix chown test crashing on systems with no root group (Alioth: #312306).
* Fixed tests when BASH_COMPLETION or TESTDIR contain spaces.
[ Raphaƫl Droz ]
diff --git a/bash_completion b/bash_completion
index 8467272a..904a8be2 100644
--- a/bash_completion
+++ b/bash_completion
@@ -90,7 +90,7 @@ complete -f -X '!*.@(@(?(e)ps|?(E)PS|pdf|PDF|dvi|DVI)?(.gz|.GZ|.bz2|.BZ2)|cb[rz]
complete -f -X '!*.@(okular|@(?(e|x)ps|?(E|X)PS|pdf|PDF|dvi|DVI|cb[rz]|CB[RZ]|djv?(u)|DJV?(U)|dvi|DVI|gif|jp?(e)g|miff|tif?(f)|pn[gm]|p[bgp]m|bmp|xpm|ico|xwd|tga|pcx|GIF|JP?(E)G|MIFF|TIF?(F)|PN[GM]|P[BGP]M|BMP|XPM|ICO|XWD|TGA|PCX|epub|EPUB|odt|ODT|fb|FB|mobi|MOBI|g3|G3|chm|CHM|fdf|FDF)?(.?(gz|GZ|bz2|BZ2)))' okular
complete -f -X '!*.@(?(e)ps|?(E)PS|pdf|PDF)' ps2pdf ps2pdf12 ps2pdf13 ps2pdf14 ps2pdfwr
complete -f -X '!*.texi*' makeinfo texi2html
-complete -f -X '!*.@(?(la)tex|?(LA)TEX|texi|TEXI|dtx|DTX|ins|INS)' tex latex slitex jadetex pdfjadetex pdftex pdflatex texi2dvi
+complete -f -X '!*.@(?(la)tex|?(LA)TEX|texi|TEXI|dtx|DTX|ins|INS|ltx|LTX)' tex latex slitex jadetex pdfjadetex pdftex pdflatex texi2dvi
complete -f -X '!*.@(mp3|MP3)' mpg123 mpg321 madplay
complete -f -X '!*@(.@(mp?(e)g|MP?(E)G|wma|avi|AVI|asf|vob|VOB|bin|dat|divx|DIVX|vcd|ps|pes|fli|flv|FLV|viv|rm|ram|yuv|mov|MOV|qt|QT|wmv|mp[234]|MP[234]|m4[pv]|M4[PV]|mkv|MKV|og[gmv]|OG[GMV]|wav|WAV|asx|ASX|mng|MNG|srt|m[eo]d|M[EO]D|s[3t]m|S[3T]M|it|IT|xm|XM)|+([0-9]).@(vdr|VDR))' xine aaxine fbxine kaffeine dragon
complete -f -X '!*.@(avi|asf|wmv)' aviplay
@@ -253,6 +253,142 @@ __reassemble_comp_words_by_ref() {
} # __reassemble_comp_words_by_ref()
+# @param $1 exclude Characters out of $COMP_WORDBREAKS which should NOT be
+# considered word breaks. This is useful for things like scp where
+# we want to return host:path and not only path, so we would pass the
+# colon (:) as $1 in this case. Bash-3 doesn't do word splitting, so this
+# ensures we get the same word on both bash-3 and bash-4.
+# @param $2 words Name of variable to return words to
+# @param $3 cword Name of variable to return cword to
+# @param $4 cur Name of variable to return current word to complete to
+# @see ___get_cword_at_cursor_by_ref()
+__get_cword_at_cursor_by_ref() {
+ # NOTE: The call to the main function ___get_cword_at_cursor_by_ref() is
+ # wrapped to make collisions with local variable names less likely.
+ local __words __cword __cur
+ ___get_cword_at_cursor_by_ref "$1" __words __cword __cur
+
+ eval $2=\( \"\${__words[@]}\" \)
+ eval $3=\$__cword
+ eval $4=\$__cur
+}
+
+
+# @param $1 exclude
+# @param $2 words Name of variable to return words to
+# @param $3 cword Name of variable to return cword to
+# @param $4 cur Name of variable to return current word to complete to
+# @note Do not call this function directly but call
+# `__get_cword_at_cursor_by_ref()' instead to make variable name collisions
+# less likely
+# @see __get_cword_at_cursor_by_ref()
+___get_cword_at_cursor_by_ref() {
+ local cword words
+ __reassemble_comp_words_by_ref "$1" words cword
+
+ local i
+ local cur="$COMP_LINE"
+ local index="$COMP_POINT"
+ for (( i = 0; i <= cword; ++i )); do
+ while [[
+ # Current word fits in $cur?
+ "${#cur}" -ge ${#words[i]} &&
+ # $cur doesn't match cword?
+ "${cur:0:${#words[i]}}" != "${words[i]}"
+ ]]; do
+ # Strip first character
+ cur="${cur:1}"
+ # Decrease cursor position
+ ((index--))
+ done
+
+ # Does found word matches cword?
+ if [[ "$i" -lt "$cword" ]]; then
+ # No, cword lies further;
+ local old_size="${#cur}"
+ cur="${cur#${words[i]}}"
+ local new_size="${#cur}"
+ index=$(( index - old_size + new_size ))
+ fi
+ done
+
+ if [[ "${words[cword]:0:${#cur}}" != "$cur" ]]; then
+ # We messed up. At least return the whole word so things keep working
+ eval $4=\"\${words[cword]}\"
+ else
+ eval $4=\"\${cur:0:\$index}\"
+ fi
+
+ eval $2=\( \"\${words[@]}\" \)
+ eval $3=\$cword
+}
+
+
+# Get the word to complete and optional previous words.
+# This is nicer than ${COMP_WORDS[$COMP_CWORD]}, since it handles cases
+# where the user is completing in the middle of a word.
+# (For example, if the line is "ls foobar",
+# and the cursor is here --------> ^
+# Also one is able to cross over possible wordbreak characters.
+# Usage: _get_comp_words_by_ref [OPTIONS] VAR1 [VAR2 [VAR3]]
+# Example usage:
+#
+# $ _get_comp_words_by_ref -n : cur prev
+#
+# Options: -n EXCLUDE Characters out of $COMP_WORDBREAKS which should NOT
+# be considered word breaks. This is useful for things like scp where
+# we want to return host:path and not only path, so we would pass the
+# colon (:) as -n option in this case. Bash-3 doesn't do word splitting,
+# so this ensures we get the same word on both bash-3 and bash-4.
+# @see __get_comp_words_by_ref
+_get_comp_words_by_ref() {
+ # NOTE: The call to the main function __get_comp_words_by_ref() is wrapped
+ # to make collisions with local variable name less likely.
+ local __words __cword __cur __var __vars
+ __get_comp_words_by_ref __words __cword __cur __vars "$@"
+ set -- "${__vars[@]}"
+ eval $1=\$__cur
+ shift
+ for __var; do
+ ((__cword--))
+ [[ ${__words[__cword]} ]] && eval $__var=\${__words[__cword]}
+ done
+}
+
+
+# @param $1 words Name of variable to return words to
+# @param $2 cword Name of variable to return cword to
+# @param $3 cur Name of variable to return current word to complete to
+# @param $4 varnames Name of variable to return array of variable names to
+# @param $@ Arguments to _get_comp_words_by_ref()
+# @note Do not call this function directly but call `_get_comp_words_by_ref()'
+# instead to make variable name collisions less likely
+# @see _get_comp_words_by_ref()
+__get_comp_words_by_ref()
+{
+ local exclude flag i OPTIND=5 # Skip first four arguments
+ local cword words cur varnames=()
+ while getopts "n:" flag "$@"; do
+ case $flag in
+ n) exclude=$OPTARG ;;
+ esac
+ done
+ varnames=( ${!OPTIND} )
+ let "OPTIND += 1"
+ while [[ $# -ge $OPTIND ]]; do
+ varnames+=( ${!OPTIND} )
+ let "OPTIND += 1"
+ done
+
+ __get_cword_at_cursor_by_ref "$exclude" words cword cur
+
+ eval $1=\( \"\${words[@]}\" \)
+ eval $2=\$cword
+ eval $3=\$cur
+ eval $4=\( \"\${varnames[@]}\" \)
+}
+
+
# Get the word to complete.
# This is nicer than ${COMP_WORDS[$COMP_CWORD]}, since it handles cases
# where the user is completing in the middle of a word.
diff --git a/doc/styleguide.txt b/doc/styleguide.txt
index 39aedf82..4dbd5732 100644
--- a/doc/styleguide.txt
+++ b/doc/styleguide.txt
@@ -37,22 +37,33 @@ be more efficient in some cases and may reduce need for nesting
conditions, and it's cleaner to write for example [[ ... && ... ]]
than [ ... ] && [ ... ], and in general [[ ]] has more features.
+Line wrapping
+-------------
+
+Try to wrap lines at 79 characters. Never go past this limit, unless
+you absolutely need to (example: a long sed regular expression, or the
+like). This also holds true for the documentation and the testsuite.
+Other files, like ChangeLog, or COPYING, are exempt from this rule.
+
+$(...) vs `...`
+---------------
+
+When you need to do some code substitution in your completion script,
+you *MUST* use the $(...) construct, rather than the `...`. The former
+is preferable because anyone, with any keyboard layout, is able to
+type it. Backticks aren't always available, without doing strange
+key combinations.
+
/////////////////////////////////////////
case/esac vs if
---------------
-line wrapping
--------------
-
quoting
-------
awk vs cut for simple cases
---------------------------
-$(...) vs `...`
----------------
-
variable and function naming
----------------------------
diff --git a/doc/testing.txt b/doc/testing.txt
index f836c494..505d0848 100644
--- a/doc/testing.txt
+++ b/doc/testing.txt
@@ -13,6 +13,11 @@ written in http://expect.nist.gov[Expect], which in turn uses
http://tcl.sourceforge.net[Tcl] -- Tool command language.
+Coding Style Guide
+------------------
+
+The bash-completion test suite tries to adhere to this
+http://wiki.tcl.tk/708[Tcl Style Guide].
Installing dependencies
@@ -62,6 +67,50 @@ Each tool has a slightly different way of loading the test fixtures, see
<<Test_context,Test context>> below.
+Completion
+~~~~~~~~~~
+
+Completion tests are spread over two directories: `completion/\*.exp` calls
+completions in `lib/completions/\*.exp`. This two-file system stems from
+bash-completion-lib (http://code.google.com/p/bash-completion-lib/, containing
+dynamic loading of completions) where tests are run twice per completion; once
+before dynamic loading and a second time after to confirm that all dynamic
+loading has gone well.
+
+For example:
+
+----
+set test "Completion via comp_load() should be installed"
+set cmd "complete -p awk"
+send "$cmd\r"
+expect {
+ -re "^$cmd\r\ncomplete -o filenames -F comp_load awk\r\n/@$" { pass "$test" }
+ -re /@ { fail "$test at prompt" }
+}
+
+
+source "lib/completions/awk.exp"
+
+
+set test "Completion via _longopt() should be installed"
+set cmd "complete -p awk"
+send "$cmd\r"
+expect {
+ -re "^$cmd\r\ncomplete -o filenames -F _longopt awk\r\n/@$" { pass "$test" }
+ -re /@ { fail "$test at prompt" }
+}
+
+
+source "lib/completions/awk.exp"
+----
+
+Looking to the completion tests from a broader perspective, every test for a
+command has two stages which are now reflected in the two files:
+
+. Tests concerning the command completions' environment (typically in
+`test/completion/foo`)
+. Tests invoking actual command completion (typically in
+`test/lib/completions/foo`)
Running the tests
diff --git a/test/Makefile.am b/test/Makefile.am
index e05abf5f..d955be8a 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -3,7 +3,6 @@ AM_RUNTESTFLAGS = --outdir log
EXTRA_DIST = completion \
config \
- fixture1 \
fixtures \
lib \
unit
diff --git a/test/completion/perldoc.exp b/test/completion/perldoc.exp
index e06e594f..d0d49b1a 100644
--- a/test/completion/perldoc.exp
+++ b/test/completion/perldoc.exp
@@ -1,3 +1 @@
-if {[assert_bash_type perl]} {
- source "lib/completions/perldoc.exp"
-}; # if
+assert_source_completions perldoc
diff --git a/test/fixtures/lftp/.lftp/bookmarks b/test/fixtures/lftp/.lftp/bookmarks
new file mode 100644
index 00000000..31ec9303
--- /dev/null
+++ b/test/fixtures/lftp/.lftp/bookmarks
@@ -0,0 +1 @@
+lftptest ftp://ftp.funet.fi/
diff --git a/test/fixture1/bar b/test/fixtures/shared/default/bar
index e69de29b..e69de29b 100644
--- a/test/fixture1/bar
+++ b/test/fixtures/shared/default/bar
diff --git a/test/fixture1/bar bar.d/foo b/test/fixtures/shared/default/bar bar.d/foo
index e69de29b..e69de29b 100644
--- a/test/fixture1/bar bar.d/foo
+++ b/test/fixtures/shared/default/bar bar.d/foo
diff --git a/test/fixture1/foo b/test/fixtures/shared/default/foo
index 257cc564..257cc564 100644
--- a/test/fixture1/foo
+++ b/test/fixtures/shared/default/foo
diff --git a/test/fixture1/foo.d/foo b/test/fixtures/shared/default/foo.d/foo
index e69de29b..e69de29b 100644
--- a/test/fixture1/foo.d/foo
+++ b/test/fixtures/shared/default/foo.d/foo
diff --git a/test/generate b/test/generate
index 5c184d56..aa282a2c 100755
--- a/test/generate
+++ b/test/generate
@@ -14,9 +14,7 @@ generate_test_completion() {
#if [ ! -f "$path" ]; then
# No, file doesn't exist; generate file
cat <<EXPECT > "$path"
-if {[assert_bash_type $1]} {
- source "lib/completions/$1.exp"
-}; # if
+assert_source_completions $1
EXPECT
#fi
} # generate_test_completion()
diff --git a/test/lib/completions/acroread.exp b/test/lib/completions/acroread.exp
index d21d0f55..4f11f905 100644
--- a/test/lib/completions/acroread.exp
+++ b/test/lib/completions/acroread.exp
@@ -1,11 +1,11 @@
proc setup {} {
save_env
- assert_bash_exec "touch fixture1/t.pdf"; # Create temporary files
+ assert_bash_exec "touch fixtures/shared/default/t.pdf"; # Create temporary files
}; # setup()
proc teardown {} {
- assert_bash_exec "rm fixture1/t.pdf"; # Remove temporary files
+ assert_bash_exec "rm fixtures/shared/default/t.pdf"; # Remove temporary files
assert_env_unmodified
}; # teardown()
@@ -13,7 +13,7 @@ proc teardown {} {
setup
-assert_complete {"bar bar.d/" foo.d/ t.pdf} "acroread fixture1/"
+assert_complete {"bar bar.d/" foo.d/ t.pdf} "acroread fixtures/shared/default/"
sync_after_int
diff --git a/test/lib/completions/cancel.exp b/test/lib/completions/cancel.exp
index ac41c282..764497c3 100644
--- a/test/lib/completions/cancel.exp
+++ b/test/lib/completions/cancel.exp
@@ -12,7 +12,7 @@ setup
# Adding a print job is successful?
-if {[assert_exec {lp -H hold fixture1/foo} job]} {
+if {[assert_exec {lp -H hold fixtures/shared/default/foo} job]} {
# Yes, adding a print-job is successful;
# Retrieve job-id, so we can cancel the job after the test
set job_id [lindex [split $job] 3]
diff --git a/test/lib/completions/cd.exp b/test/lib/completions/cd.exp
index d4c06d9d..58233575 100644
--- a/test/lib/completions/cd.exp
+++ b/test/lib/completions/cd.exp
@@ -12,7 +12,7 @@ setup
set test "Tab should complete"
-assert_complete {"bar bar.d/" foo.d/} "cd fixture1/" $test
+assert_complete {"bar bar.d/" foo.d/} "cd fixtures/shared/default/" $test
sync_after_int
@@ -20,13 +20,13 @@ sync_after_int
set test "Tab should complete cd at cursor position"
# Try completion
-set cmd "cd fixture1/foo"
+set cmd "cd fixtures/shared/default/foo"
append cmd \002\002\002; # \002 = ^B = Move cursor left in bash emacs mode
#append cmd \033\0133D; # Escape-[-D = Cursor left
send "$cmd\t"
expect {
- -re "cd fixture1/foo\b\b\b\r\n(\.svn/ +|)bar bar.d/ +foo.d/ *(\.svn/ *|)\r\n/@cd fixture1/foo\b\b\b$" { pass "$test" }
- -re "^cd fixture1/foo\b\b\bfoo.d/foo\b\b\b$" { fail "$test: Wrong cursor position" }
+ -re "cd fixtures/shared/default/foo\b\b\b\r\n(\.svn/ +|)bar bar.d/ +foo.d/ *(\.svn/ *|)\r\n/@cd fixtures/shared/default/foo\b\b\b$" { pass "$test" }
+ -re "^cd fixtures/shared/default/foo\b\b\bfoo.d/foo\b\b\b$" { fail "$test: Wrong cursor position" }
-re /@ { unresolved "$test at prompt" }
default { unresolved "$test" }
}; # expect
@@ -38,7 +38,7 @@ sync_after_int
set test "Tab should complete CDPATH"
# Set CDPATH
assert_bash_exec "CDPATH=\$PWD";
-assert_complete "fixture1/foo.d/" "cd fixture1/fo" $test
+assert_complete "fixtures/shared/default/foo.d/" "cd fixtures/shared/default/fo" $test
sync_after_int
# Reset CDPATH
assert_bash_exec "unset CDPATH"
diff --git a/test/lib/completions/chown.exp b/test/lib/completions/chown.exp
index 480f6743..953b2b02 100644
--- a/test/lib/completions/chown.exp
+++ b/test/lib/completions/chown.exp
@@ -10,30 +10,28 @@ proc teardown {} {
setup
-assert_complete_any "chown "
+set users [exec bash -c "compgen -A user"]
+assert_complete $users "chown "
+
+
sync_after_int
-# All the tests use the root:root user and group. They're assumed to exist.
-set fulluser "root"
-set fullgroup "root"
-
-# Partial username is assumed to be unambiguous.
-set partuser "roo"
-set partgroup "roo"
-
-# Skip tests if root:root not available or if roo:roo matches multiple
-# users/groups
-#
-# compgen -A is used because it's a bash builtin and available everywhere.
-# The || true part prevents exec from throwing an exception if nothing is
-# found.
-if {[exec bash -c "compgen -A user $partuser || true" | wc -l] > 1 ||
- [exec bash -c "compgen -A user $fulluser || true" | wc -l] != 1 ||
- [exec bash -c "compgen -A group $partgroup || true" | wc -l] > 1 ||
- [exec bash -c "compgen -A group $fullgroup || true" | wc -l] != 1} {
- untested "Not running complex chown tests."
-} else {
+# Find user/group suitable for testing.
+set failed_find_unique_completion 0
+foreach ug {user group} {
+ # compgen -A is used because it's a bash builtin and available everywhere.
+ # The || true part prevents exec from throwing an exception if nothing is
+ # found (very very unlikely).
+ set list [split [exec bash -c "compgen -A $ug || true"] "\n"]
+ if {![find_unique_completion_pair $list part$ug full$ug]} {
+ untested "Not running complex chown tests; no suitable test $ug found."
+ set failed_find_unique_completion 1
+ }
+}
+
+# These tests require an unique completion.
+if {!$failed_find_unique_completion} {
assert_complete $fulluser "chown $partuser"
sync_after_int
diff --git a/test/lib/completions/find.exp b/test/lib/completions/find.exp
index 0fb5c62c..8009fddc 100644
--- a/test/lib/completions/find.exp
+++ b/test/lib/completions/find.exp
@@ -29,7 +29,7 @@ sync_after_int
set test "-wholename should complete files/dirs"
-set dir fixture1
+set dir fixtures/shared/default
set files [split [exec bash -c "cd $dir && ls -p"] "\n"]
assert_complete_dir $files "find -wholename " $dir
diff --git a/test/lib/completions/lftp.exp b/test/lib/completions/lftp.exp
index 4bdde376..a49a1935 100644
--- a/test/lib/completions/lftp.exp
+++ b/test/lib/completions/lftp.exp
@@ -1,4 +1,5 @@
proc setup {} {
+ assert_bash_exec {HOME=$TESTDIR/fixtures/lftp}
save_env
}; # setup()
@@ -11,7 +12,10 @@ proc teardown {} {
setup
-assert_complete_any "lftp "
+set expected [get_hosts]
+# `lftptest' is defined in ./fixtures/lftp/.lftp/bookmarks
+lappend expected lftptest
+assert_complete $expected "lftp "
sync_after_int
diff --git a/test/lib/completions/perl.exp b/test/lib/completions/perl.exp
index 4b463383..2199c994 100644
--- a/test/lib/completions/perl.exp
+++ b/test/lib/completions/perl.exp
@@ -18,7 +18,7 @@ sync_after_int
set test "Second argument should file complete"
-set cmd "perl foo fixture1/f"
+set cmd "perl foo fixtures/shared/default/f"
send "$cmd\t"
expect {
-re "^$cmd\r\nfoo +foo.d/ *\r\n/@${cmd}oo$" { pass "$test" }
@@ -31,7 +31,7 @@ sync_after_int
set test "-I without space should complete directories"
-set cmd "perl -Ifixture1/"
+set cmd "perl -Ifixtures/shared/default/"
send "$cmd\t"
expect {
-re "^$cmd\r\nbar bar.d/ +foo.d/ *\r\n/@$cmd$" { pass "$test" }
@@ -44,7 +44,7 @@ sync_after_int
set test "-I with space should complete directories"
-set cmd "perl -I fixture1/"
+set cmd "perl -I fixtures/shared/default/"
send "$cmd\t"
expect {
-re "^$cmd\r\nbar bar.d/ +foo.d/ *\r\n/@$cmd$" { pass "$test" }
@@ -57,7 +57,7 @@ sync_after_int
set test "-x without space should complete directories"
-set cmd "perl -xfixture1/b"
+set cmd "perl -xfixtures/shared/default/b"
send "$cmd\t"
expect {
-re "^${cmd}ar\\\\ bar.d/ *$" { pass "$test" }
@@ -70,7 +70,7 @@ sync_after_int
set test "-x with space should complete directories"
-set cmd "perl -x fixture1/b"
+set cmd "perl -x fixtures/shared/default/b"
send "$cmd\t"
expect {
-re "^${cmd}ar\\\\ bar.d/ *$" { pass "$test" }
diff --git a/test/lib/completions/sbcl-mt.exp b/test/lib/completions/sbcl-mt.exp
index 1188f197..45e81f79 100644
--- a/test/lib/completions/sbcl-mt.exp
+++ b/test/lib/completions/sbcl-mt.exp
@@ -11,7 +11,7 @@ proc teardown {} {
setup
-assert_complete {bar "bar bar.d/" foo foo.d/} "sbcl-mt fixture1/"
+assert_complete {bar "bar bar.d/" foo foo.d/} "sbcl-mt fixtures/shared/default/"
sync_after_int
diff --git a/test/lib/completions/sbcl.exp b/test/lib/completions/sbcl.exp
index d5f2c8c2..b68b2dce 100644
--- a/test/lib/completions/sbcl.exp
+++ b/test/lib/completions/sbcl.exp
@@ -11,7 +11,7 @@ proc teardown {} {
setup
-assert_complete {bar "bar bar.d/" foo foo.d/} "sbcl fixture1/"
+assert_complete {bar "bar bar.d/" foo foo.d/} "sbcl fixtures/shared/default/"
sync_after_int
diff --git a/test/lib/completions/screen.exp b/test/lib/completions/screen.exp
index f486b463..62c57362 100644
--- a/test/lib/completions/screen.exp
+++ b/test/lib/completions/screen.exp
@@ -18,7 +18,7 @@ sync_after_int
set test "-c should complete files/dirs"
-set dir fixture1
+set dir fixtures/shared/default
set prompt "/$dir/@"
assert_bash_exec "cd $dir" "" $prompt
set cmd "screen -c "
diff --git a/test/lib/completions/ssh.exp b/test/lib/completions/ssh.exp
index 8a0d88d2..91955c1d 100644
--- a/test/lib/completions/ssh.exp
+++ b/test/lib/completions/ssh.exp
@@ -52,30 +52,17 @@ sync_after_int
set test "First argument shouldn't complete with commands"
-# NOTE: This test assumes the machine running this test has a command "bash"
-# but no host named "bash" ...
+# NOTE: This test assumes there's a command "bash" and no host named "bash"
set cmd "ssh bas"
-send "$cmd\t"
-expect -ex "$cmd"
-expect {
- -timeout 1
- # In case multiple commands `bas*' - besides `bash' - are completed
- -re "^\r\n.*bash.*\r\n/@$cmd$" { fail "$test" }
- # In case the single command `bash' is completed
- -re "h $" { fail "$test" }
- # In case the hostname `bash_completion' is completed.
- # See `scp' tests in `lib/completions/scp.exp'
- -re "h_completion $" { pass "$test" }
- -re ".+" { unresolved "$test" }
- timeout { pass "$test" }
-}; # expect
+assert_complete [get_known_hosts "bas"] $cmd $test
sync_after_int
set test "First argument should complete partial hostname"
-assert_complete_partial [get_hosts] ssh "" $test /@ 20 [list "ltrim_colon_completions"]
+assert_complete_partial [get_hosts] ssh "" $test /@ 20 \
+ [list "ltrim_colon_completions"]
sync_after_int
diff --git a/test/lib/completions/sudo.exp b/test/lib/completions/sudo.exp
index 3dc98da3..1299a6da 100644
--- a/test/lib/completions/sudo.exp
+++ b/test/lib/completions/sudo.exp
@@ -11,7 +11,7 @@ proc teardown {} {
setup
-assert_complete "fixture1/foo.d/" "sudo cd fixture1/fo"
+assert_complete "fixtures/shared/default/foo.d/" "sudo cd fixtures/shared/default/fo"
sync_after_int
diff --git a/test/lib/library.exp b/test/lib/library.exp
index fa554c73..00dd8469 100644
--- a/test/lib/library.exp
+++ b/test/lib/library.exp
@@ -79,18 +79,22 @@ proc assert_bash_type {command} {
# @result boolean True if successful, False if not
proc assert_bash_list {expected cmd {test ""} {prompt /@} {size 20}} {
if {$test == ""} {set test "$cmd should show expected output"}
- send "$cmd\r"
- expect -ex "$cmd\r\n"
-
- if {[match_items $expected $test $prompt $size]} {
- expect {
- -re $prompt { pass "$test" }
- -re eof { unresolved "eof" }
- }; # expect
+ if {[llength $expected] == 0} {
+ assert_no_output $cmd $test $prompt
} else {
- fail "$test"
- }; # if
-}; # assert_bash_list()
+ send "$cmd\r"
+ expect -ex "$cmd\r\n"
+
+ if {[match_items $expected $test $prompt $size]} {
+ expect {
+ -re $prompt { pass "$test" }
+ -re eof { unresolved "eof" }
+ }
+ } else {
+ fail "$test"
+ }
+ }
+}
proc assert_bash_list_dir {expected cmd dir {test ""} {prompt /@} {size 20}} {
@@ -451,6 +455,43 @@ proc assert_no_complete {{cmd} {test ""}} {
}; # assert_no_complete()
+# Check that no output is generated on a certain command.
+# @param string $cmd The command to attempt to complete.
+# @param string $test Optional parameter with test name.
+# @param string $prompt (optional) Bash prompt. Default is "/@"
+proc assert_no_output {{cmd} {test ""} {prompt /@}} {
+ if {[string length $test] == 0} {
+ set test "$cmd shouldn't generate output"
+ }
+
+ send "$cmd\r"
+ expect -ex "$cmd"
+
+ expect {
+ -re "^\r\n$prompt$" { pass "$test" }
+ default { fail "$test" }
+ timeout { fail "$test" }
+ }
+}
+
+
+# Source/run file with additional tests if completion for the specified command
+# is installed in bash.
+# @param string $command Command to check completion availability for.
+# @param string $file (optional) File to source/run. Default is
+# "lib/completions/$cmd.exp".
+proc assert_source_completions {command {file ""}} {
+ if {[is_bash_completion_installed_for $command]} {
+ if {[string length $file] == 0} {
+ set file "lib/completions/$command.exp"
+ }
+ source $file
+ } else {
+ untested $command
+ }
+}; # assert_source_completions()
+
+
# Sort list.
# `exec sort' is used instead of `lsort' to achieve exactly the
# same sort order as in bash.
@@ -462,10 +503,12 @@ proc bash_sort {items} {
# Get 'known' hostnames. Looks also in ssh's 'known_hosts' files.
+# @param string cword (optional) Word, hosts should start with.
# @return list Hostnames
# @see get_hosts()
-proc get_known_hosts {} {
- assert_bash_exec {_known_hosts_real ''; echo_array COMPREPLY} {} /@ result
+proc get_known_hosts {{cword ''}} {
+ assert_bash_exec "_known_hosts_real '$cword'; echo_array COMPREPLY" \
+ {} /@ result
return $result
}; # get_known_hosts()
@@ -532,6 +575,24 @@ proc init_tcl_bash_globals {} {
}; # init_tcl_bash_globals()
+# Check whether completion is installed for the specified command by executing
+# `complete -p ...' in bash.
+# @param string $command Command to check completion availability for.
+# @return boolean True (1) if completion is installed, False (0) if not.
+proc is_bash_completion_installed_for {command} {
+ set test "$command should have completion installed in bash"
+ set cmd "complete -p $command &> /dev/null && echo -n 0 || echo -n 1"
+ send "$cmd\r"
+ expect "$cmd\r\n"
+ expect {
+ -ex 0 { set result true }
+ -ex 1 { set result false }
+ }
+ expect "/@"
+ return $result
+}; # is_bash_completion_installed_for()
+
+
# Detect if test suite is running under Cygwin/Windows
proc is_cygwin {} {
expr {[string first [string tolower [exec uname -s]] cygwin] >= 0}
@@ -684,6 +745,73 @@ proc split_words_bash {line} {
}; # split_words_bash()
+# Given a list of items this proc finds a (part, full) pair so that when
+# completing from $part $full will be the only option.
+#
+# Arguments:
+# list The list of full completions.
+# partName Output parameter for the partial string.
+# fullName Output parameter for the full string, member of item.
+#
+# Results:
+# 1, or 0 if no suitable result was found.
+proc find_unique_completion_pair {{list} {partName} {fullName}} {
+ upvar $partName part
+ upvar $fullName full
+ set bestscore 0
+ set list [lsort $list]
+ set n [llength $list]
+ for {set i 0} {$i < $n} {incr i} {
+ set cur [lindex $list $i]
+ set curlen [string length $cur]
+
+ set prev [lindex $list [expr {$i - 1}]]
+ set next [lindex $list [expr {$i + 1}]]
+ set diffprev [expr {$prev == ""}]
+ set diffnext [expr {$next == ""}]
+
+ # Analyse each item of the list and look for the minimum length of the
+ # partial prefix which is distinct from both $next and $prev. The list
+ # is sorted so the prefix will be unique in the entire list.
+ #
+ # In the worst case we analyse every character in the list 3 times.
+ # That's actually very fast, sorting could take more.
+ for {set j 0} {$j < $curlen} {incr j} {
+ set curchar [string index $cur $j]
+ if {!$diffprev && [string index $prev $j] != $curchar} {
+ set diffprev 1
+ }
+ if {!$diffnext && [string index $next $j] != $curchar} {
+ set diffnext 1
+ }
+ if {$diffnext && $diffprev} {
+ break
+ }
+ }
+
+ # At the end of the loop $j is the index of last character of
+ # the unique partial prefix. The length is one plus that.
+ set parlen [expr {$j + 1}]
+ if {$parlen >= $curlen} {
+ continue
+ }
+
+ # Try to find the most "readable pair"; look for a long pair where
+ # $part is about half of $full.
+ if {$parlen < $curlen / 2} {
+ set parlen [expr {$curlen / 2}]
+ }
+ set score [expr {$curlen - $parlen}]
+ if {$score > $bestscore} {
+ set bestscore $score
+ set part [string range $cur 0 [expr {$parlen - 1}]]
+ set full $cur
+ }
+ }
+ return [expr {$bestscore != 0}]
+}
+
+
# Start bash running as test environment.
proc start_bash {} {
global TESTDIR TOOL_EXECUTABLE spawn_id
diff --git a/test/unit/_get_comp_words_by_ref.exp b/test/unit/_get_comp_words_by_ref.exp
new file mode 100644
index 00000000..67862b3a
--- /dev/null
+++ b/test/unit/_get_comp_words_by_ref.exp
@@ -0,0 +1,339 @@
+proc setup {} {
+ assert_bash_exec {unset COMP_CWORD COMP_LINE COMP_POINT COMP_WORDS}
+ save_env
+}; # setup()
+
+
+proc teardown {} {
+ assert_bash_exec {unset COMP_CWORD COMP_LINE COMP_POINT COMP_WORDS cur prev prev2}
+ # Delete 'COMP_WORDBREAKS' occupying two lines
+ assert_env_unmodified {
+ /COMP_WORDBREAKS=/{N
+ d
+ }
+ }
+}; # teardown()
+
+
+setup
+
+
+set test "_get_comp_words_by_ref should run without errors"
+assert_bash_exec {_get_comp_words_by_ref cur > /dev/null} $test
+
+
+sync_after_int
+
+
+# See also ./lib/completions/alias.exp. Here `_get_cword' is actually tested
+# by moving the cursor left into the current word.
+
+
+set test "a b|"; # | = cursor position
+set cmd {COMP_WORDS=(a b); COMP_CWORD=1; COMP_LINE='a b'; COMP_POINT=3; _get_comp_words_by_ref cur prev; echo "$cur $prev"}
+assert_bash_list {"b a"} $cmd $test
+
+
+sync_after_int
+
+
+set test "a |"; # | = cursor position
+set cmd {COMP_WORDS=(a); COMP_CWORD=1; COMP_LINE='a '; COMP_POINT=2; _get_comp_words_by_ref cur prev; echo "$cur $prev"}
+assert_bash_list {" a"} $cmd $test
+
+
+sync_after_int
+
+
+set test "a b |"; # | = cursor position
+set cmd {COMP_WORDS=(a b ''); COMP_CWORD=2; COMP_LINE='a b '; COMP_POINT=4; _get_comp_words_by_ref cur prev prev2; echo "$cur $prev $prev2"}
+assert_bash_list {" b a"} $cmd $test
+
+
+sync_after_int
+
+
+set test "a b | with WORDBREAKS -= :"; # | = cursor position
+set cmd {COMP_WORDS=(a b ''); COMP_CWORD=2; COMP_LINE='a b '; COMP_POINT=4; _get_comp_words_by_ref -n : cur; printf %s "$cur"}
+assert_bash_list {} $cmd $test
+
+
+sync_after_int
+
+
+set test "a b|c"; # | = cursor position
+set cmd {COMP_WORDS=(a bc); COMP_CWORD=1; COMP_LINE='a bc'; COMP_POINT=3; _get_comp_words_by_ref cur prev; echo "$cur $prev"}
+assert_bash_list {"b a"} $cmd $test
+
+
+sync_after_int
+
+
+set test {a b\ c| should return b\ c}; # | = cursor position
+set cmd {COMP_WORDS=(a 'b\ c'); COMP_CWORD=1; COMP_LINE='a b\ c'; COMP_POINT=6; _get_comp_words_by_ref cur prev; echo "$cur $prev"}
+assert_bash_list {"b\\ c a"} $cmd $test
+
+
+sync_after_int
+
+
+set test {a b\| c should return b\ }; # | = cursor position
+set cmd {COMP_WORDS=(a 'b\ c'); COMP_CWORD=1; COMP_LINE='a b\ c'; COMP_POINT=4; _get_comp_words_by_ref cur prev; echo "$cur $prev"}
+assert_bash_list {"b\\ a"} $cmd $test
+
+
+sync_after_int
+
+
+set test {a "b\|}; #"# | = cursor position
+set cmd {COMP_WORDS=(a '"b\'); COMP_CWORD=1; COMP_LINE='a "b\'; COMP_POINT=5; _get_comp_words_by_ref cur prev; echo "$cur $prev"}
+assert_bash_list {"\"b\\ a"} $cmd $test
+
+
+sync_after_int
+
+
+set test {a 'b c|}; # | = cursor position
+if {
+ [lindex $::BASH_VERSINFO 0] == 4 &&
+ [lindex $::BASH_VERSINFO 1] == 0 &&
+ [lindex $::BASH_VERSINFO 2] < 35
+} {
+ set cmd {COMP_WORDS=(a "'" b c); COMP_CWORD=3}
+} else {
+ set cmd {COMP_WORDS=(a "'b c"); COMP_CWORD=1}
+}; # if
+append cmd {; COMP_LINE="a 'b c"; COMP_POINT=6; _get_comp_words_by_ref cur prev; echo "$cur $prev"}
+send "$cmd\r"
+expect -ex "$cmd\r\n"
+expect {
+ -ex "'b c a\r\n/@" { pass "$test" }
+ -ex "c b\r\n/@" {
+ if {
+ [lindex $::BASH_VERSINFO 0] == 4 &&
+ [lindex $::BASH_VERSINFO 1] == 0 &&
+ [lindex $::BASH_VERSINFO 2] < 35
+ } {xfail "$test"} {fail "$test"}
+ }
+}; # expect
+
+
+sync_after_int
+
+
+set test {a "b c|}; #"# | = cursor position
+if {
+ [lindex $::BASH_VERSINFO 0] == 4 &&
+ [lindex $::BASH_VERSINFO 1] == 0 &&
+ [lindex $::BASH_VERSINFO 2] < 35
+} {
+ set cmd {COMP_WORDS=(a "\"" b c); COMP_CWORD=3}
+} else {
+ set cmd {COMP_WORDS=(a "\"b c"); COMP_CWORD=1}
+}; # if
+append cmd {; COMP_LINE="a \"b c"; COMP_POINT=6}
+assert_bash_exec $cmd
+set cmd {_get_comp_words_by_ref cur prev; echo "$cur $prev"};
+send "$cmd\r"
+expect -ex "$cmd\r\n"
+expect {
+ -ex "\"b c a\r\n/@" { pass "$test" }
+ -ex "c b\r\n/@" {
+ if {
+ [lindex $::BASH_VERSINFO 0] == 4 &&
+ [lindex $::BASH_VERSINFO 1] == 0 &&
+ [lindex $::BASH_VERSINFO 2] < 35
+ } {xfail "$test"} {fail "$test"}
+ }
+}; # expect
+
+
+sync_after_int
+
+
+set test {a b:c| with WORDBREAKS += :}; # | = cursor position
+if {[lindex $::BASH_VERSINFO 0] <= 3} {
+ set cmd {COMP_WORDS=(a "b:c"); COMP_CWORD=1}
+ set expected {"b:c a"}
+} else {
+ set cmd {add_comp_wordbreak_char :; COMP_WORDS=(a b : c); COMP_CWORD=3}
+ set expected {"c :"}
+}; # if
+append cmd {; COMP_LINE='a b:c'; COMP_POINT=5}
+# NOTE: Split-send cmd to prevent backspaces (\008) in output
+assert_bash_exec $cmd $test
+set cmd {_get_comp_words_by_ref cur prev; echo "$cur $prev"}
+assert_bash_list $expected $cmd $test
+
+
+sync_after_int
+
+
+set test {a b:c| with WORDBREAKS -= :}; # | = cursor position
+if {[lindex $::BASH_VERSINFO 0] <= 3} {
+ set cmd {COMP_WORDS=(a "b:c"); COMP_CWORD=1}
+} else {
+ set cmd {COMP_WORDS=(a b : c); COMP_CWORD=3}
+}; # if
+append cmd {; COMP_LINE='a b:c'; COMP_POINT=5}
+assert_bash_exec $cmd $test
+set cmd {_get_comp_words_by_ref -n : cur prev; echo "$cur $prev"}
+assert_bash_list {"b:c a"} $cmd $test
+
+
+sync_after_int
+
+
+set test {a b c:| with WORDBREAKS -= :}; # | = cursor position
+if {[lindex $::BASH_VERSINFO 0] <= 3} {
+ set cmd {COMP_WORDS=(a b c:); COMP_CWORD=2}
+} else {
+ set cmd {COMP_WORDS=(a b c :); COMP_CWORD=3}
+}; # if
+append cmd {; COMP_LINE='a b c:'; COMP_POINT=6}
+assert_bash_exec $cmd $test
+set cmd {_get_comp_words_by_ref -n : cur prev; echo "$cur $prev $prev2"}
+assert_bash_list {"c: b a"} $cmd $test
+
+
+sync_after_int
+
+
+set test {a :| with WORDBREAKS -= : should return :}; # | = cursor position
+set cmd {COMP_WORDS=(a :); COMP_CWORD=1; COMP_LINE='a :'; COMP_POINT=3}
+assert_bash_exec $cmd
+set cmd {_get_comp_words_by_ref -n : cur prev; echo "$cur $prev"}
+assert_bash_list {": a"} $cmd $test
+
+
+sync_after_int
+
+
+set test {a b::| with WORDBREAKS -= : should return b::}; # | = cursor position
+if {[lindex $::BASH_VERSINFO 0] <= 3} {
+ set cmd {COMP_WORDS=(a "b::"); COMP_CWORD=1}
+} else {
+ set cmd {COMP_WORDS=(a b ::); COMP_CWORD=2}
+}; # if
+append cmd {; COMP_LINE='a b::'; COMP_POINT=5}
+assert_bash_exec $cmd
+set cmd {_get_comp_words_by_ref -n : cur prev; echo "$cur $prev"}
+assert_bash_list {"b:: a"} $cmd $test
+
+
+sync_after_int
+
+
+# This test makes sure `_get_cword' doesn't use `echo' to return it's value,
+# because -n might be interpreted by `echo' and thus will not be returned.
+set test "a -n| should return -n"; # | = cursor position
+set cmd {COMP_WORDS=(a -n); COMP_CWORD=1; COMP_LINE='a -n'; COMP_POINT=4}
+assert_bash_exec $cmd
+set cmd {_get_comp_words_by_ref cur; printf %s $cur}
+assert_bash_list -n $cmd $test
+
+
+sync_after_int
+
+
+set test {a b>c| should return c}; # | = cursor position
+set cmd {COMP_WORDS=(a b \> c); COMP_CWORD=3; COMP_LINE='a b>c'; COMP_POINT=5}
+assert_bash_exec $cmd
+set cmd {_get_comp_words_by_ref cur prev; echo "$cur"}
+assert_bash_list c $cmd $test
+
+
+sync_after_int
+
+
+set test {a b=c| should return b=c (bash-3) or c (bash-4)}; # | = cursor position
+if {[lindex $::BASH_VERSINFO] <= 3} {
+ set cmd {COMP_WORDS=(a "b=c"); COMP_CWORD=1}
+ set expected b=c
+} else {
+ set cmd {COMP_WORDS=(a b = c); COMP_CWORD=3}
+ set expected c
+}; # if
+append cmd {; COMP_LINE='a b=c'; COMP_POINT=5}
+assert_bash_exec $cmd
+set cmd {_get_comp_words_by_ref cur prev; echo "$cur"}
+assert_bash_list $expected $cmd $test
+
+
+sync_after_int
+
+
+set test {a *| should return *}; # | = cursor position
+set cmd {COMP_WORDS=(a \*); COMP_CWORD=1; COMP_LINE='a *'; COMP_POINT=4}
+assert_bash_exec $cmd
+set cmd {_get_comp_words_by_ref cur; echo "$cur"}
+assert_bash_list * $cmd $test
+
+
+sync_after_int
+
+
+set test {a $(b c| should return $(b c}; # | = cursor position
+set cmd {COMP_WORDS=(a '$(b c'); COMP_CWORD=1; COMP_LINE='a $(b c'; COMP_POINT=7}
+assert_bash_exec $cmd
+set cmd {_get_comp_words_by_ref cur; printf %s "$cur"}
+send "$cmd\r"
+expect -ex "$cmd\r\n"
+expect {
+ -ex "\$(b c/@" { pass "$test" }
+ # Expected failure on bash-4
+ -ex "c/@" { xfail "$test" }
+}; # expect
+
+
+sync_after_int
+
+
+set test {a $(b c\ d| should return $(b c\ d}; # | = cursor position
+set cmd {COMP_WORDS=(a '$(b c\ d'); COMP_CWORD=1; COMP_LINE='a $(b c\ d'; COMP_POINT=10}
+assert_bash_exec $cmd
+set cmd {_get_comp_words_by_ref cur; printf %s "$cur"}
+#assert_bash_list {{$(b\ c\\\ d}} $cmd $test
+send "$cmd\r"
+expect -ex "$cmd\r\n"
+expect {
+ -ex "\$(b c\\ d/@" { pass "$test" }
+ # Expected failure on bash-4
+ -ex "c\\ d/@" { xfail "$test" }
+}; # expect
+
+
+sync_after_int
+
+
+set test {a 'b&c| should return 'b&c}; # | = cursor position
+if {
+ [lindex $::BASH_VERSINFO 0] == 4 &&
+ [lindex $::BASH_VERSINFO 1] == 0 &&
+ [lindex $::BASH_VERSINFO 2] < 35
+} {
+ set cmd {COMP_WORDS=(a "'" b "&" c); COMP_CWORD=4}
+} else {
+ set cmd {COMP_WORDS=(a "'b&c"); COMP_CWORD=1}
+}; # if
+append cmd {; COMP_LINE="a 'b&c"; COMP_POINT=6}
+assert_bash_exec $cmd
+set cmd {_get_comp_words_by_ref cur prev; printf %s "$cur"}
+send "$cmd\r"
+expect -ex "$cmd\r\n"
+expect {
+ -ex "'b&c/@" { pass "$test" }
+ -ex "c/@" {
+ if {
+ [lindex $::BASH_VERSINFO 0] == 4 &&
+ [lindex $::BASH_VERSINFO 1] == 0 &&
+ [lindex $::BASH_VERSINFO 2] < 35
+ } {xfail "$test"} {fail "$test"}
+ }
+}; # expect
+
+
+sync_after_int
+
+
+teardown
diff --git a/test/unit/find_unique_completion_pair.exp b/test/unit/find_unique_completion_pair.exp
new file mode 100644
index 00000000..ec7f040d
--- /dev/null
+++ b/test/unit/find_unique_completion_pair.exp
@@ -0,0 +1,37 @@
+# Note: This test actually tests a function in the test library. It doesn't
+# need bash running; but it doesn't hurt either.
+
+# Run one test. Look below for usage.
+proc test_find_ucp {{list} {epart} {econt} {eret 1}} {
+ set efull "$epart$econt"
+ set rret [find_unique_completion_pair $list rpart rfull]
+ if {$eret != $rret} {
+ if {$eret} {
+ fail "find_unique_completion_pair: Nothing found for {$list}"
+ } else {
+ fail "find_unique_completion_pair: Expected failure for {$list}"
+ }
+ } elseif {!$eret} {
+ pass "find_unique_completion_pair: No results for list {$list}"
+ } elseif {$rpart != $epart || $rfull != $efull} {
+ fail "find_unique_completion_pair: Got \"$rpart\", \"$rfull\" \
+ instead of \"$epart\", \"$efull\" for list {$list}"
+ } else {
+ pass "find_unique_completion_pair: Got \"$epart\", \"$efull\" \
+ for list {$list}"
+ }
+}
+
+test_find_ucp {a} 0 0 0
+test_find_ucp {ab} a b
+test_find_ucp {a ab abcd abc} 0 0 0
+test_find_ucp {a ab abcde abc} abcd e
+test_find_ucp {user1 user2} 0 0 0
+test_find_ucp {root username2 username1} ro ot
+test_find_ucp {root username21 username2} ro ot
+test_find_ucp {long_user_name lang_user_name long_usor_name} lang_us er_name
+test_find_ucp {lang_user_name1 long_user_name lang_user_name long_usor_name} \
+ long_use r_name
+test_find_ucp {root username} user name
+test_find_ucp {a aladin} ala din
+test_find_ucp {ala aladin} alad in