diff options
author | Gabriel F. T. Gomes <gabriel@inconstante.net.br> | 2020-08-03 18:43:13 -0300 |
---|---|---|
committer | Gabriel F. T. Gomes <gabriel@inconstante.net.br> | 2020-08-03 18:43:13 -0300 |
commit | 95623d39d6029ba78ec96ad5ea08e9ac12629b91 (patch) | |
tree | ea0fe36eb5e6f40e0a1f765d44c4b0c0b2bfb089 /test | |
parent | 019f3cc463db63abc6460f97deb488deec43840b (diff) | |
download | bash-completion-95623d39d6029ba78ec96ad5ea08e9ac12629b91.tar.gz |
New upstream version 2.11upstream/2.11upstream
Diffstat (limited to 'test')
173 files changed, 2620 insertions, 2831 deletions
diff --git a/test/.gitignore b/test/.gitignore index aa9a8096..c428b949 100644 --- a/test/.gitignore +++ b/test/.gitignore @@ -1,7 +1,2 @@ -log/ tmp/ -dbg.log -xtrace.log -site.exp -site.bak pytestdebug.log diff --git a/test/Makefile.am b/test/Makefile.am index 003ec132..591c8f7c 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,21 +1,14 @@ -AUTOMAKE_OPTIONS = dejagnu -DEJATOOL = completion install unit -AM_RUNTESTFLAGS = --outdir log --ignore $(PACKAGE).log - SUBDIRS = t -EXTRA_DIST = completion \ - config \ +EXTRA_DIST = config \ fixtures \ - lib \ - setup.cfg \ - unit + setup.cfg all: - $(MKDIR_P) log tmp + $(MKDIR_P) tmp CLEANFILES = \ fixtures/make/extra_makefile clean-local: - $(RM) -rf log tmp + $(RM) -rf tmp diff --git a/test/completion/alias.exp b/test/completion/alias.exp deleted file mode 100644 index ee7cf4bc..00000000 --- a/test/completion/alias.exp +++ /dev/null @@ -1 +0,0 @@ -assert_source_completions alias diff --git a/test/completion/cd.exp b/test/completion/cd.exp deleted file mode 100644 index 94c3c598..00000000 --- a/test/completion/cd.exp +++ /dev/null @@ -1 +0,0 @@ -assert_source_completions cd diff --git a/test/completion/chown.exp b/test/completion/chown.exp deleted file mode 100644 index 53d497c2..00000000 --- a/test/completion/chown.exp +++ /dev/null @@ -1 +0,0 @@ -assert_source_completions chown diff --git a/test/completion/finger.exp b/test/completion/finger.exp deleted file mode 100644 index 7c7b8a26..00000000 --- a/test/completion/finger.exp +++ /dev/null @@ -1 +0,0 @@ -assert_source_completions finger diff --git a/test/completion/scp.exp b/test/completion/scp.exp deleted file mode 100644 index e025a9dd..00000000 --- a/test/completion/scp.exp +++ /dev/null @@ -1 +0,0 @@ -assert_source_completions scp diff --git a/test/completion/sftp.exp b/test/completion/sftp.exp deleted file mode 100644 index 448cd218..00000000 --- a/test/completion/sftp.exp +++ /dev/null @@ -1 +0,0 @@ -assert_source_completions sftp diff --git a/test/completion/slapt-get.exp b/test/completion/slapt-get.exp deleted file mode 100644 index 6c37d523..00000000 --- a/test/completion/slapt-get.exp +++ /dev/null @@ -1 +0,0 @@ -assert_source_completions slapt-get diff --git a/test/completion/slapt-src.exp b/test/completion/slapt-src.exp deleted file mode 100644 index 90abfd5f..00000000 --- a/test/completion/slapt-src.exp +++ /dev/null @@ -1 +0,0 @@ -assert_source_completions slapt-src diff --git a/test/completion/ssh.exp b/test/completion/ssh.exp deleted file mode 100644 index 0477cba5..00000000 --- a/test/completion/ssh.exp +++ /dev/null @@ -1 +0,0 @@ -assert_source_completions ssh diff --git a/test/completion/sudo.exp b/test/completion/sudo.exp deleted file mode 100644 index 2a8015ff..00000000 --- a/test/completion/sudo.exp +++ /dev/null @@ -1 +0,0 @@ -assert_source_completions sudo diff --git a/test/completion/umount.exp b/test/completion/umount.exp deleted file mode 100644 index 39c4d114..00000000 --- a/test/completion/umount.exp +++ /dev/null @@ -1 +0,0 @@ -assert_source_completions umount diff --git a/test/completion/upgradepkg.exp b/test/completion/upgradepkg.exp deleted file mode 100644 index 4b181a86..00000000 --- a/test/completion/upgradepkg.exp +++ /dev/null @@ -1 +0,0 @@ -assert_source_completions upgradepkg diff --git a/test/completion/xhost.exp b/test/completion/xhost.exp deleted file mode 100644 index 159782b2..00000000 --- a/test/completion/xhost.exp +++ /dev/null @@ -1 +0,0 @@ -assert_source_completions xhost diff --git a/test/config/bashrc b/test/config/bashrc index dad96335..141dddc5 100644 --- a/test/config/bashrc +++ b/test/config/bashrc @@ -1,7 +1,7 @@ # bashrc file for bash-completion test suite # Note that we do some initialization that would be too late to do here in -# library.exp's start_bash() and conftest.py. +# conftest.py. # Use emacs key bindings set -o emacs @@ -9,6 +9,9 @@ set -o emacs # Use bash strict mode set -o posix +# Raise error on uninitialized variables +set -o nounset + # Unset `command_not_found_handle' as defined on Debian/Ubuntu, because this # troubles and slows down testing unset -f command_not_found_handle @@ -32,7 +35,10 @@ export BASH_COMPLETION_USER_FILE=/dev/null # but simple xspec completions are only installed if a separate one is not # found in any completion dirs. Therefore we also point the "system" dirs to # locations that should not yield valid completions and helpers paths either. -export BASH_COMPLETION_USER_DIR=$(cd "$SRCDIR/.."; pwd) +export BASH_COMPLETION_USER_DIR=$( + cd "$SRCDIR/.." || exit 1 + pwd +) # /var/empty isn't necessarily actually always empty :P export BASH_COMPLETION_COMPAT_DIR=/var/empty/bash_completion.d export XDG_DATA_DIRS=/var/empty @@ -44,8 +50,11 @@ unset -v \ COMP_KNOWN_HOSTS_WITH_HOSTFILE \ COMP_TAR_INTERNAL_PATHS -# Load bash testsuite helper functions -. $SRCDIR/lib/library.sh +# @param $1 Char to add to $COMP_WORDBREAKS +add_comp_wordbreak_char() +{ + [[ "${COMP_WORDBREAKS//[^$1]/}" ]] || COMP_WORDBREAKS+=$1 +} # Local variables: # mode: shell-script diff --git a/test/config/default.exp b/test/config/default.exp deleted file mode 100644 index 246499b4..00000000 --- a/test/config/default.exp +++ /dev/null @@ -1,21 +0,0 @@ -# Set default expect fallback routines -expect_after { - eof { - if {[info exists test]} { - fail "$test at eof" - } elseif {[info level] > 0} { - fail "[info level 1] at eof" - } else { - fail "eof" - } - } - timeout { - if {[info exists test]} { - fail "$test at timeout" - } elseif {[info level] > 0} { - fail "[info level 1] at timeout" - } else { - fail "timeout" - } - } -} diff --git a/test/config/inputrc b/test/config/inputrc index 5992491a..da896f56 100644 --- a/test/config/inputrc +++ b/test/config/inputrc @@ -1,17 +1,21 @@ -# Readline init file for DejaGnu testsuite +# Readline init file for bash-completion test suite # See: info readline - # Press TAB once (instead of twice) to auto-complete +# Press TAB once (instead of twice) to auto-complete set show-all-if-ambiguous on - # No bell. No ^G in output + +# No bell. No ^G in output set bell-style none - # Don't query user about viewing the number of possible completions + +# Don't query user about viewing the number of possible completions set completion-query-items -1 - # Display completions sorted horizontally, not vertically -set print-completions-horizontally on - # Don't use pager when showing completions + +# Don't use pager when showing completions set page-completions off +# Print each completion on its own line +set completion-display-width 0 + # Local variables: # mode: shell-script # End: diff --git a/test/docker/docker-script.sh b/test/docker/docker-script.sh index 681f2429..b3f351fa 100755 --- a/test/docker/docker-script.sh +++ b/test/docker/docker-script.sh @@ -1,20 +1,5 @@ #!/bin/sh -ex -if [ $DIST = tools ]; then - rc=0 - perlcritic helpers/perl; rc=$((rc+$?)) - perltidy -nst -nse helpers/perl; rc=$((rc+$?)) - if [ -e helpers/perl.ERR ]; then - cat helpers/perl.ERR - rc=$((rc+1)) - fi - flake8 helpers/python test test/generate; rc=$((rc+$?)) - black --check -t py27 -t py33 -t py34 -t py35 -t py36 -t py37 -t py38 \ - helpers/python; rc=$((rc+$?)) - black --check test test/generate; rc=$((rc+$?)) - exit $rc -fi - if [ "$BSD" ]; then PATH=/usr/local/lib/bsd-bin:$PATH export PATH @@ -28,5 +13,4 @@ autoreconf -i make -j xvfb-run make distcheck \ - PYTESTFLAGS="--numprocesses=auto --dist=loadfile" \ - RUNTESTFLAGS="--all --verbose" + PYTESTFLAGS="--verbose --numprocesses=auto --dist=loadfile" diff --git a/test/fixtures/_known_hosts_real/.ssh/config_asterisk_1 b/test/fixtures/_known_hosts_real/.ssh/config_asterisk_1 new file mode 100644 index 00000000..fc09eb03 --- /dev/null +++ b/test/fixtures/_known_hosts_real/.ssh/config_asterisk_1 @@ -0,0 +1 @@ +Host asterisk_1 diff --git a/test/fixtures/_known_hosts_real/.ssh/config_asterisk_2 b/test/fixtures/_known_hosts_real/.ssh/config_asterisk_2 new file mode 100644 index 00000000..42243ad2 --- /dev/null +++ b/test/fixtures/_known_hosts_real/.ssh/config_asterisk_2 @@ -0,0 +1 @@ +Host asterisk_2 diff --git a/test/fixtures/_known_hosts_real/.ssh/config_question_mark b/test/fixtures/_known_hosts_real/.ssh/config_question_mark new file mode 100644 index 00000000..08e1201f --- /dev/null +++ b/test/fixtures/_known_hosts_real/.ssh/config_question_mark @@ -0,0 +1 @@ +Host question_mark diff --git a/test/fixtures/_known_hosts_real/config b/test/fixtures/_known_hosts_real/config index 1231dd79..fe3fb54a 100644 --- a/test/fixtures/_known_hosts_real/config +++ b/test/fixtures/_known_hosts_real/config @@ -1,7 +1,7 @@ - UserKnownHostsFile fixtures/_known_hosts_real/known_hosts + UserKnownHostsFile _known_hosts_real/known_hosts # Unindented -Host gee jar +Host gee* jar?this-part-we-do-not-complete-at-least-yet HostName %h.example.com # Indented - Host hus # With comment + Host hus%%eth0 !negated #not-a-comment diff --git a/test/fixtures/_known_hosts_real/config_include b/test/fixtures/_known_hosts_real/config_include index fe6f537e..a1ae7633 100644 --- a/test/fixtures/_known_hosts_real/config_include +++ b/test/fixtures/_known_hosts_real/config_include @@ -3,3 +3,5 @@ Include ~/config_full_path # Include with relative path Include config_relative_path +# Include with wildcards, and more than one on same row +Include config_asterisk* config_?uestion_mark diff --git a/test/fixtures/_known_hosts_real/config_tilde b/test/fixtures/_known_hosts_real/config_tilde index 0893515b..4181aaf5 100644 --- a/test/fixtures/_known_hosts_real/config_tilde +++ b/test/fixtures/_known_hosts_real/config_tilde @@ -1,4 +1,4 @@ # With quotes and tilde -UserKnownHostsFile "~/fixtures/_known_hosts_real/known_hosts2" +UserKnownHostsFile "~/_known_hosts_real/known_hosts2" # Without quotes, with tilde, and another on the same line -UserKnownHostsFile ~/fixtures/_known_hosts_real/known_hosts3 fixtures/_known_hosts_real/known_hosts4 +UserKnownHostsFile ~/_known_hosts_real/known_hosts3 _known_hosts_real/known_hosts4 diff --git a/test/fixtures/slackware/etc/slapt-get/slapt-getrc b/test/fixtures/_known_hosts_real/gee-filename-canary index e69de29b..e69de29b 100644 --- a/test/fixtures/slackware/etc/slapt-get/slapt-getrc +++ b/test/fixtures/_known_hosts_real/gee-filename-canary diff --git a/test/fixtures/_known_hosts_real/localhost_config b/test/fixtures/_known_hosts_real/localhost_config index 1e751338..30b6623b 100644 --- a/test/fixtures/_known_hosts_real/localhost_config +++ b/test/fixtures/_known_hosts_real/localhost_config @@ -1 +1 @@ -UserKnownHostsFile fixtures/_known_hosts_real/localhost_hosts +UserKnownHostsFile _known_hosts_real/localhost_hosts diff --git a/test/fixtures/_known_hosts_real/spaced conf b/test/fixtures/_known_hosts_real/spaced conf index f484f0e3..566b92c2 100644 --- a/test/fixtures/_known_hosts_real/spaced conf +++ b/test/fixtures/_known_hosts_real/spaced conf @@ -1,9 +1,8 @@ # Unindented Host gee - UserKnownHostsFile "fixtures/_known_hosts_real/spaced known_hosts" + UserKnownHostsFile "_known_hosts_real/spaced known_hosts" # Indented - Host hus # With comment - UserKnownHostsFile "fixtures/_known_hosts_real/known_hosts2" - + Host hus #not-a-comment + UserKnownHostsFile "_known_hosts_real/known_hosts2" diff --git a/test/fixtures/_longopt/various.txt b/test/fixtures/_longopt/various.txt new file mode 100644 index 00000000..04c2c256 --- /dev/null +++ b/test/fixtures/_longopt/various.txt @@ -0,0 +1,5 @@ +--- +----nonono +--foo_bar +--foo- +--foo=bar diff --git a/test/fixtures/ant/.gitignore b/test/fixtures/ant/.gitignore index 459f1019..3a08258b 100644 --- a/test/fixtures/ant/.gitignore +++ b/test/fixtures/ant/.gitignore @@ -1,2 +1 @@ -.ant-targets-build.xml -.ant-targets-named-build.xml +.ant-targets-*.xml diff --git a/test/fixtures/slackware/etc/slapt-get/slapt-srcrc b/test/fixtures/dpkg/bash-completion-test-nonsubject.txt index e69de29b..e69de29b 100644 --- a/test/fixtures/slackware/etc/slapt-get/slapt-srcrc +++ b/test/fixtures/dpkg/bash-completion-test-nonsubject.txt diff --git a/test/fixtures/lftp/.lftp/bookmarks b/test/fixtures/lftp/.lftp/bookmarks index 31ec9303..074f8120 100644 --- a/test/fixtures/lftp/.lftp/bookmarks +++ b/test/fixtures/lftp/.lftp/bookmarks @@ -1 +1,3 @@ lftptest ftp://ftp.funet.fi/ +spacetest ftp://ftp.sunet.se/ +badbookmark diff --git a/test/fixtures/lilo/lilo.conf b/test/fixtures/lilo/lilo.conf new file mode 100644 index 00000000..c8901758 --- /dev/null +++ b/test/fixtures/lilo/lilo.conf @@ -0,0 +1,34 @@ +# global options: +boot=/dev/hda +prompt +timeout=150 +lba32 +compact +vga=normal +root=/dev/hda1 +read-only +menu-title=" John's Computer " +# +### bootable kernel images ### +image=/boot/vmlinuz-2.6.29-1-i386 + label=try + initrd=/boot/initrd.img-2.6.29-1-i386 +#image=/boot/vmlinuz-2.4.33-1-i386 +# label=2.4.33 +image=/tamu/vmlinuz + label=tamu + initrd=/tamu/initrd.img + root=/dev/hdb2 + vga=ask +# +### other operating systems ### +other=/dev/hda3 + label=PCDOS + boot-as=0x80 # must be C: +other=/dev/hdb1 + label=WinXP + boot-as=0x80 # must be C: +other=/dev/hdb5 + label=oldDOS + loader=chain + table=/dev/hdb5 diff --git a/test/fixtures/nmap/nmap-h.txt b/test/fixtures/nmap/nmap-h.txt new file mode 100644 index 00000000..0301d374 --- /dev/null +++ b/test/fixtures/nmap/nmap-h.txt @@ -0,0 +1,114 @@ +Nmap 7.60 ( https://nmap.org ) +Usage: nmap [Scan Type(s)] [Options] {target specification} +TARGET SPECIFICATION: + Can pass hostnames, IP addresses, networks, etc. + Ex: scanme.nmap.org, microsoft.com/24, 192.168.0.1; 10.0.0-255.1-254 + -iL <inputfilename>: Input from list of hosts/networks + -iR <num hosts>: Choose random targets + --exclude <host1[,host2][,host3],...>: Exclude hosts/networks + --excludefile <exclude_file>: Exclude list from file +HOST DISCOVERY: + -sL: List Scan - simply list targets to scan + -sn: Ping Scan - disable port scan + -Pn: Treat all hosts as online -- skip host discovery + -PS/PA/PU/PY[portlist]: TCP SYN/ACK, UDP or SCTP discovery to given ports + -PE/PP/PM: ICMP echo, timestamp, and netmask request discovery probes + -PO[protocol list]: IP Protocol Ping + -n/-R: Never do DNS resolution/Always resolve [default: sometimes] + --dns-servers <serv1[,serv2],...>: Specify custom DNS servers + --system-dns: Use OS's DNS resolver + --traceroute: Trace hop path to each host +SCAN TECHNIQUES: + -sS/sT/sA/sW/sM: TCP SYN/Connect()/ACK/Window/Maimon scans + -sU: UDP Scan + -sN/sF/sX: TCP Null, FIN, and Xmas scans + --scanflags <flags>: Customize TCP scan flags + -sI <zombie host[:probeport]>: Idle scan + -sY/sZ: SCTP INIT/COOKIE-ECHO scans + -sO: IP protocol scan + -b <FTP relay host>: FTP bounce scan +PORT SPECIFICATION AND SCAN ORDER: + -p <port ranges>: Only scan specified ports + Ex: -p22; -p1-65535; -p U:53,111,137,T:21-25,80,139,8080,S:9 + --exclude-ports <port ranges>: Exclude the specified ports from scanning + -F: Fast mode - Scan fewer ports than the default scan + -r: Scan ports consecutively - don't randomize + --top-ports <number>: Scan <number> most common ports + --port-ratio <ratio>: Scan ports more common than <ratio> +SERVICE/VERSION DETECTION: + -sV: Probe open ports to determine service/version info + --version-intensity <level>: Set from 0 (light) to 9 (try all probes) + --version-light: Limit to most likely probes (intensity 2) + --version-all: Try every single probe (intensity 9) + --version-trace: Show detailed version scan activity (for debugging) +SCRIPT SCAN: + -sC: equivalent to --script=default + --script=<Lua scripts>: <Lua scripts> is a comma separated list of + directories, script-files or script-categories + --script-args=<n1=v1,[n2=v2,...]>: provide arguments to scripts + --script-args-file=filename: provide NSE script args in a file + --script-trace: Show all data sent and received + --script-updatedb: Update the script database. + --script-help=<Lua scripts>: Show help about scripts. + <Lua scripts> is a comma-separated list of script-files or + script-categories. +OS DETECTION: + -O: Enable OS detection + --osscan-limit: Limit OS detection to promising targets + --osscan-guess: Guess OS more aggressively +TIMING AND PERFORMANCE: + Options which take <time> are in seconds, or append 'ms' (milliseconds), + 's' (seconds), 'm' (minutes), or 'h' (hours) to the value (e.g. 30m). + -T<0-5>: Set timing template (higher is faster) + --min-hostgroup/max-hostgroup <size>: Parallel host scan group sizes + --min-parallelism/max-parallelism <numprobes>: Probe parallelization + --min-rtt-timeout/max-rtt-timeout/initial-rtt-timeout <time>: Specifies + probe round trip time. + --max-retries <tries>: Caps number of port scan probe retransmissions. + --host-timeout <time>: Give up on target after this long + --scan-delay/--max-scan-delay <time>: Adjust delay between probes + --min-rate <number>: Send packets no slower than <number> per second + --max-rate <number>: Send packets no faster than <number> per second +FIREWALL/IDS EVASION AND SPOOFING: + -f; --mtu <val>: fragment packets (optionally w/given MTU) + -D <decoy1,decoy2[,ME],...>: Cloak a scan with decoys + -S <IP_Address>: Spoof source address + -e <iface>: Use specified interface + -g/--source-port <portnum>: Use given port number + --proxies <url1,[url2],...>: Relay connections through HTTP/SOCKS4 proxies + --data <hex string>: Append a custom payload to sent packets + --data-string <string>: Append a custom ASCII string to sent packets + --data-length <num>: Append random data to sent packets + --ip-options <options>: Send packets with specified ip options + --ttl <val>: Set IP time-to-live field + --spoof-mac <mac address/prefix/vendor name>: Spoof your MAC address + --badsum: Send packets with a bogus TCP/UDP/SCTP checksum +OUTPUT: + -oN/-oX/-oS/-oG <file>: Output scan in normal, XML, s|<rIpt kIddi3, + and Grepable format, respectively, to the given filename. + -oA <basename>: Output in the three major formats at once + -v: Increase verbosity level (use -vv or more for greater effect) + -d: Increase debugging level (use -dd or more for greater effect) + --reason: Display the reason a port is in a particular state + --open: Only show open (or possibly open) ports + --packet-trace: Show all packets sent and received + --iflist: Print host interfaces and routes (for debugging) + --append-output: Append to rather than clobber specified output files + --resume <filename>: Resume an aborted scan + --stylesheet <path/URL>: XSL stylesheet to transform XML output to HTML + --webxml: Reference stylesheet from Nmap.Org for more portable XML + --no-stylesheet: Prevent associating of XSL stylesheet w/XML output +MISC: + -6: Enable IPv6 scanning + -A: Enable OS detection, version detection, script scanning, and traceroute + --datadir <dirname>: Specify custom Nmap data file location + --send-eth/--send-ip: Send using raw ethernet frames or IP packets + --privileged: Assume that the user is fully privileged + --unprivileged: Assume the user lacks raw socket privileges + -V: Print version number + -h: Print this help summary page. +EXAMPLES: + nmap -v -A scanme.nmap.org + nmap -v -sn 192.168.0.0/16 10.0.0.0/8 + nmap -v -iR 10000 -Pn -p 80 +SEE THE MAN PAGE (https://nmap.org/book/man.html) FOR MORE OPTIONS AND EXAMPLES diff --git a/test/fixtures/perl/Devel/BashCompletion.pm b/test/fixtures/perl/Devel/BashCompletion.pm index 0afc6045..f8e829d8 100644 --- a/test/fixtures/perl/Devel/BashCompletion.pm +++ b/test/fixtures/perl/Devel/BashCompletion.pm @@ -1 +1,3 @@ +use strict; +use warnings; 1; diff --git a/test/fixtures/perldoc/BashCompletionModule.pm b/test/fixtures/perldoc/BashCompletionModule.pm index e69de29b..b9208f34 100644 --- a/test/fixtures/perldoc/BashCompletionModule.pm +++ b/test/fixtures/perldoc/BashCompletionModule.pm @@ -0,0 +1,4 @@ +package BashCompletionModule; +use strict; +use warnings; +1; diff --git a/test/fixtures/pytest/test_async.py b/test/fixtures/pytest/test_async.py new file mode 100644 index 00000000..48f91e08 --- /dev/null +++ b/test/fixtures/pytest/test_async.py @@ -0,0 +1,17 @@ +"""Async function pytest completion fixture.""" + + +async def test_positive(): + pass + + +async def non_test_negative(): + pass + + +class Testing: + async def test_positive(self): + pass + + async def non_test_negative(self): + pass diff --git a/test/fixtures/ssh-copy-id/.ssh/id_rsa b/test/fixtures/ssh-copy-id/.ssh/id_rsa new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/test/fixtures/ssh-copy-id/.ssh/id_rsa diff --git a/test/lib/completion.exp b/test/lib/completion.exp deleted file mode 100644 index 88e92d50..00000000 --- a/test/lib/completion.exp +++ /dev/null @@ -1,24 +0,0 @@ -source $::srcdir/lib/library.exp - - -proc completion_exit {} { - send "\rexit\r" -} - - -proc completion_init {test_file_name} { - # Call completion_start() only once - if {! [info exists ::BASH_VERSINFO]} { - completion_start - } -} - - -proc completion_start {} { - start_interactive_test -} - - -proc completion_version {} { - puts "$::TESTDIR, bash-$::BASH_VERSION" -} diff --git a/test/lib/completions/alias.exp b/test/lib/completions/alias.exp deleted file mode 100644 index c41417f1..00000000 --- a/test/lib/completions/alias.exp +++ /dev/null @@ -1,32 +0,0 @@ -proc setup {} { - assert_bash_exec "unalias -a"; # Remove all aliases - assert_bash_exec "alias foo=bar" - assert_bash_exec "alias bar='foo foo'" - save_env -} - - -proc teardown {} { - assert_bash_exec "unalias -a"; # Remove all aliases - assert_env_unmodified -} - - -setup - - -set test "Tab should complete alias at cursor position" - # Try completion -set cmd "alias foo" -append cmd \002\002\002; # \002 = ^B = Move cursor left in bash emacs mode -send "$cmd\t" -expect { - -re "^alias foo\b\b\b\r\nbar +foo *\r\n/@alias foo\b\b\b$" { pass "$test" } - -re "^alias foo\b\b\bfoo=foo\b\b\b$" { fail "$test: Wrong cursor position" } - -re /@ { unresolved "$test" } - default { unresolved "$test" } -} -sync_after_int - - -teardown diff --git a/test/lib/completions/cd.exp b/test/lib/completions/cd.exp deleted file mode 100644 index ded094c3..00000000 --- a/test/lib/completions/cd.exp +++ /dev/null @@ -1,31 +0,0 @@ -proc setup {} { - save_env -} - - -proc teardown {} { - assert_env_unmodified {/OLDPWD=/d} -} - - -setup - - -set test "Tab should complete cd at cursor position" - # Try completion -set cmd "cd $::srcdir/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 $::srcdir/fixtures/shared/default/foo\b\b\b\r\n(\.svn/ +|)bar bar.d/ +foo.d/ *(\.svn/ *|)\r\n/@cd $::srcdir/fixtures/shared/default/foo\b\b\b$" { pass "$test" } - -re "^cd $::srcdir/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" } -} - - -sync_after_int - - -teardown diff --git a/test/lib/completions/chown.exp b/test/lib/completions/chown.exp deleted file mode 100644 index 792d52a9..00000000 --- a/test/lib/completions/chown.exp +++ /dev/null @@ -1,43 +0,0 @@ -proc setup {} { - # fake root command to get all users/groups completed at least for now - assert_bash_exec {root_command=sudo} - save_env -} - -proc teardown {} { - assert_env_unmodified -} - - -setup - - -# 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} { - - foreach prefix { - "funky\\ user:" "funky.user:" "funky\\.user:" "fu\\ nky.user:" - "f\\ o\\ o\\.\\bar:" "foo\\_b\\ a\\.r\\ :" - } { - set test "Check preserve special chars in $prefix$partgroup<TAB>" - #assert_complete_into "chown $prefix$partgroup" "chown $prefix$fullgroup " $test - assert_complete $prefix$fullgroup "chown $prefix$partgroup" $test - sync_after_int - } -} - - -teardown diff --git a/test/lib/completions/finger.exp b/test/lib/completions/finger.exp deleted file mode 100644 index c055f354..00000000 --- a/test/lib/completions/finger.exp +++ /dev/null @@ -1,41 +0,0 @@ -proc setup {} { - save_env -} - - -proc teardown {} { - assert_env_unmodified -} - - -setup - - -sync_after_int - - -set test "Tab should complete partial hostname" -# Build string list of hostnames, starting with the character of the first -# host, unless host starts with a COMP_WORDBREAKS character, e.g. a colon (:). -# Hosts starting with a COMP_WORDBREAKS character are left out because these -# are exceptional cases, requiring specific tests. -set hosts {} -set char "" -foreach h [get_known_hosts] { - set first [string range $h 0 0] - if {$char == "" && [string first $first $::COMP_WORDBREAKS] == -1} {set char $first} - if {$char != ""} { - # Only append unique hostnames starting with $char - if {$first == $char && [lsearch -exact $hosts "test@$h"] == -1} { - # Prefix hosts with username 'test@' - lappend hosts "test@$h" - } - } -} -assert_complete $hosts "finger test@$char" $test -expect-cmd-minus "test@$char" - - -sync_after_int - - -teardown diff --git a/test/lib/completions/scp.exp b/test/lib/completions/scp.exp deleted file mode 100644 index 1497a7fb..00000000 --- a/test/lib/completions/scp.exp +++ /dev/null @@ -1,117 +0,0 @@ -proc setup {} { - save_env - # NOTE: Changing dir to $SRCDIR is necessary because file locations in the - # ssh config files (e.g. UserKnownHostsFile) are relative to $SRCDIR. - assert_bash_exec {cd $SRCDIR/fixtures/scp} -} - - -proc teardown {} { - assert_bash_exec {cd $TESTDIR} - assert_env_unmodified { - /BASH_LINENO=/d - /BASH_SOURCE=/d - /OLDPWD=/d - } -} - - -setup - - -set test "Tab should complete remote pwd" -set host bash_completion - - # Retrieving home directory (host_pwd) from ssh-host `bash_completion' - # yields error? -if { - [catch { - exec -- ssh -o "Batchmode yes" -o "ConnectTimeout 1" $host pwd 2>>/dev/null - } host_pwd] -} { - # Yes, retrieving pwd from ssh yields error; reset `host_pwd' - # Indicate host pwd is unknown and test is unsupported - # NOTE: To support this test, set the hostname "bash_completion" - # in `$HOME/.ssh/config' or `/etc/ssh_config' - set host_pwd "" - unsupported $test -} - - - # Try completion -set cmd "scp $host:" -send "$cmd\t" -sync_after_tab -expect { - -re "^$cmd$host_pwd.*$" { pass "$test" } - -re /@ { unresolved "$test at prompt" } -} - - -sync_after_int - - -set test "Tab should complete known-hosts" - - # Build string list of expected completions - # Get hostnames and give them a colon (:) suffix - # Hosts `gee' and `hut' are defined in ./fixtures/scp/config - # Hosts `blah', `doo' and `ike' are defined in ./fixtures/scp/known_hosts -set expected {} -foreach host [get_hosts] { - lappend expected "$host:" -} -lappend expected blah: doo: gee: hut: ike: - # Append local filenames -lappend expected config known_hosts "spaced\\ \\ conf" -assert_complete $expected "scp -F config " $test - - -sync_after_int - - -set test "-F without space shouldn't error" - # Try completion -set cmd "scp -F" -send "$cmd\t " -expect { - -re "^${cmd}bash: option requires an argument -- F" { fail "$test" } - -re "^$cmd\r\n.*\r\n/@" { pass "$test" } - -re /@ { unresolved "$test at prompt" } - default { unresolved "$test" } -} - - -sync_after_int - - -set test "Config file containing space should work" - # Build string list of expected completions - # Get hostnames and give them a colon (:) suffix -set expected {} -foreach host [get_hosts] { - lappend expected "$host:" -} - # Hosts `gee', `hus' and `jar' are defined in "./fixtures/scp/spaced conf" - # Hosts `blah', `doo' and `ike' are defined in ./fixtures/scp/known_hosts -lappend expected blah: doo: gee: hus: ike: jar: - # Append local filenames -lappend expected config known_hosts "spaced\\ \\ conf" -set cmd "scp -F 'spaced conf' " -send "$cmd\t" -expect -ex "$cmd\r\n" -if {[match_items [lsort -unique $expected] -bash-sort]} { - expect { - -re /@ { pass "$test" } - -re eof { unresolved "eof" } - default { fail "$test" } - } -} -sync_after_int -assert_bash_exec {cd "$TESTDIR"} - - -sync_after_int - - -teardown diff --git a/test/lib/completions/sftp.exp b/test/lib/completions/sftp.exp deleted file mode 100644 index c5c0919f..00000000 --- a/test/lib/completions/sftp.exp +++ /dev/null @@ -1,60 +0,0 @@ -proc setup {} { - save_env - # NOTE: Changing dir to $SRCDIR is necessary because file locations in the - # ssh config files (e.g. UserKnownHostsFile) are relative to $SRCDIR. - assert_bash_exec {cd $SRCDIR/fixtures/sftp} -} - - -proc teardown {} { - assert_bash_exec {cd $TESTDIR} - assert_env_unmodified { - /BASH_LINENO=/d - /BASH_SOURCE=/d - /OLDPWD=/d - } -} - - -setup - - - # Build string list of expected completions -set expected [get_hosts] - # Hosts `gee' and `hut' are defined in ./fixtures/sftp/config - # Hosts `10.10.10.10', `doo' and `ike' are defined in ./fixtures/sftp/known_hosts -lappend expected 10.10.10.10 doo gee hut ike -assert_complete $expected "sftp -F config " - - -sync_after_int - - -set test "-F without space shouldn't error" - # Try completion -set cmd "sftp -F" -send "$cmd\t " -expect { - -re "^${cmd}bash: option requires an argument -- F" { fail "$test" } - -re "^$cmd\r\n.*\r\n/@" { pass "$test" } - -re /@ { unresolved "$test at prompt" } - default { unresolved "$test" } -} - - -sync_after_int - - - # Build string list of expected completions - # Get hostnames and give them a colon (:) suffix -set expected [get_hosts] - # Hosts `gee' and `jar' are defined in "./fixtures/sftp/spaced conf" - # Hosts `10.10.10.10', `doo' and `ike' are defined in ./fixtures/sftp/known_hosts -lappend expected 10.10.10.10 doo gee ike jar -assert_complete $expected "sftp -F spaced\\ \\ conf " - - -sync_after_int - - -teardown diff --git a/test/lib/completions/slapt-get.exp b/test/lib/completions/slapt-get.exp deleted file mode 100644 index 4522610d..00000000 --- a/test/lib/completions/slapt-get.exp +++ /dev/null @@ -1,32 +0,0 @@ -proc setup {} { - save_env -} - - -proc teardown {} { - assert_env_unmodified -} - - -setup - - -set test "--install should complete available packages" -set config $::srcdir/fixtures/slackware/etc/slapt-get/slapt-getrc -set workdir [file normalize $::srcdir/fixtures/slackware/var/slapt-get/] -set pkg_data "$workdir/package_data" -# write simple config -set f [open $config w] -puts $f "WORKINGDIR=$workdir" -puts $f "SOURCE=file:///home/" -close $f -set packages [split [exec bash -c "sed -n \ - '/^PACKAGE NAME:/{s/^PACKAGE NAME: \\{1,\\}\\(.*\\).t\[gbxl\]z/\\1/;p}' \ - $pkg_data"] "\n"] -assert_complete $packages "slapt-get -c $config --install " $test - - -sync_after_int - - -teardown diff --git a/test/lib/completions/slapt-src.exp b/test/lib/completions/slapt-src.exp deleted file mode 100644 index b66385ef..00000000 --- a/test/lib/completions/slapt-src.exp +++ /dev/null @@ -1,29 +0,0 @@ -proc setup {} { - save_env -} - - -proc teardown {} { - assert_env_unmodified -} - - -setup - - -set test "--install should complete available packages" -set config $::srcdir/fixtures/slackware/etc/slapt-get/slapt-srcrc -set workdir [file normalize $::srcdir/fixtures/slackware/usr/src/slapt-src/] -set slb_data "$workdir/slackbuilds_data" -set f [open $config w]; puts $f "BUILDDIR=$workdir"; close $f -set slackbuilds [split [exec bash -c "sed -n \ - -e '/^SLACKBUILD NAME: /{s/^SLACKBUILD NAME: \\{1,\\}//;p}' \ - -e '/^SLACKBUILD VERSION: /{s/^SLACKBUILD VERSION: \\{1,\\}//;p}' \ - $slb_data | sed -e 'N;s/\\n/:/'"] "\n"] -assert_complete $slackbuilds "slapt-src --config $config --install " $test - - -sync_after_int - - -teardown diff --git a/test/lib/completions/ssh.exp b/test/lib/completions/ssh.exp deleted file mode 100644 index 1702371c..00000000 --- a/test/lib/completions/ssh.exp +++ /dev/null @@ -1,61 +0,0 @@ -proc setup {} { - save_env - # NOTE: Changing dir to $SRCDIR is necessary because file locations in the - # ssh config files (e.g. UserKnownHostsFile) are relative to $SRCDIR. - assert_bash_exec {cd $SRCDIR/fixtures/ssh} -} - - -proc teardown {} { - assert_bash_exec {cd $TESTDIR} - assert_env_unmodified { - /BASH_LINENO=/d - /BASH_SOURCE=/d - /OLDPWD=/d - } -} - - -setup - - -set test "-F without space shouldn't error" - # Try completion -set cmd "ssh -F" -send "$cmd\t " -set expected "^$cmd $" -expect { - -re "^${cmd}bash: option requires an argument -- F" { fail "$test" } - -re "^$cmd\r\n.*\r\n/@" { pass "$test" } - -re /@ { unresolved "$test at prompt" } - default { unresolved "$test" } -} - - -sync_after_int - - -set test "First argument should complete partial hostname" -# Build string list of hostnames, starting with the character of the first -# host, unless host starts with a COMP_WORDBREAKS character, e.g. a colon (:). -# Hosts starting with a COMP_WORDBREAKS character are left out because these -# are exceptional cases, requiring specific tests. -set hosts {} -set char "" -foreach h [get_known_hosts] { - set first [string range $h 0 0] - if {$char == "" && [string first $first $::COMP_WORDBREAKS] == -1} {set char $first} - if {$char != ""} { - # Only append unique hostnames starting with $char - if {$first == $char && [lsearch -exact $hosts "$h"] == -1} { - lappend hosts "$h" - } - } -} -assert_complete $hosts "ssh $char" $test -ltrim-colon-completions -expect-cmd-minus "$char" - - -sync_after_int - - -teardown diff --git a/test/lib/completions/sudo.exp b/test/lib/completions/sudo.exp deleted file mode 100644 index 73e485d6..00000000 --- a/test/lib/completions/sudo.exp +++ /dev/null @@ -1,42 +0,0 @@ -proc setup {} { - save_env -} - - -proc teardown {} { - assert_env_unmodified {/OLDPWD/d} -} - - -setup - - -# 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} { - - foreach prefix { - "funky\\ user:" "funky.user:" "funky\\.user:" "fu\\ nky.user:" - "f\\ o\\ o\\.\\bar:" "foo\\_b\\ a\\.r\\ :" - } { - set test "Check preserve special chars in $prefix$partgroup<TAB>" - #assert_complete_into "chown $prefix$partgroup" "chown $prefix$fullgroup " $test - assert_complete $prefix$fullgroup "sudo chown $prefix$partgroup" $test - sync_after_int - } -} - - -teardown diff --git a/test/lib/completions/umount.exp b/test/lib/completions/umount.exp deleted file mode 100644 index 03144355..00000000 --- a/test/lib/completions/umount.exp +++ /dev/null @@ -1,95 +0,0 @@ -# umount completion from fstab can't be tested directly because it -# (correctly) uses absolute paths. So we create a custom completion which -# reads from a file in our text fixture instead. -proc setup_dummy_mnt {} { - assert_bash_exec {unset COMPREPLY cur} - assert_bash_exec {unset -f _mnt} - - assert_bash_exec { \ - _mnt() { \ - local cur=$(_get_cword); \ - _linux_fstab $(_get_pword) < "$SRCDIRABS/fixtures/mount/test-fstab"; \ - }; \ - complete -F _mnt mnt \ - } -} - - -proc teardown_dummy_mnt {} { - assert_bash_exec {unset COMPREPLY cur} - assert_bash_exec {unset -f _mnt} - assert_bash_exec {complete -r mnt} -} - - -proc setup {} { - save_env - setup_dummy_mnt -} - - -proc teardown {} { - teardown_dummy_mnt - assert_env_unmodified {/OLDPWD/d} -} - - -setup - - -set test "Testing internal __linux_fstab_unescape function for umount" -# One round of slashes is for bash. -assert_bash_exec {var=one\'two\\040three\\} -assert_bash_exec {__linux_fstab_unescape var} -set cmd {echo $var} -send "$cmd\r" -expect { - -ex "$cmd\r\none'two three\\" { pass $test } -# default { fail $test } -} -sync_after_int -assert_bash_exec {unset var} - - -sync_after_int - - -# Begin testing through mnt (see setup_dummy_mnt). -assert_complete {/mnt/nice-test-path} {mnt /mnt/nice-test-p} -sync_after_int - -assert_complete {/mnt/nice\ test-path} {mnt /mnt/nice\ test-p} -sync_after_int - -assert_complete {/mnt/nice\$test-path} {mnt /mnt/nice\$test-p} -sync_after_int - -assert_complete {/mnt/nice\ test\\path} {mnt /mnt/nice\ test\\p} -sync_after_int - -assert_complete {{/mnt/nice\ test\\path} {/mnt/nice\ test-path}} \ - {mnt /mnt/nice\ } "" -expect-cmd-minus {/mnt/nice\ } -sync_after_int - -assert_complete {/mnt/nice\$test-path} {mnt /mnt/nice\$} -sync_after_int - -assert_complete {/mnt/nice\'test-path} {mnt /mnt/nice\'} -sync_after_int - -assert_complete {/mnt/other\'test\ path} {mnt /mnt/other} -sync_after_int - -assert_complete {Ubuntu\ Karmic} {mnt -L Ubu} -sync_after_int - -assert_complete {Debian-it\'s\ awesome} {mnt -L Deb} -sync_after_int - -# This does not work. Proper support for this requires smarter parsing of -# $COMP_LINE and it's not worth doing just for umount. -#assert_complete {$'/mnt/nice\ntest-path'} {mnt $'/mnt/nice\n} -#sync_after_int - - -teardown diff --git a/test/lib/completions/upgradepkg.exp b/test/lib/completions/upgradepkg.exp deleted file mode 100644 index ffb4ba28..00000000 --- a/test/lib/completions/upgradepkg.exp +++ /dev/null @@ -1,26 +0,0 @@ -proc setup {} { - save_env -} - - -proc teardown {} { - assert_env_unmodified {/OLDPWD=/d} -} - - -setup - - -set test "should complete *.t\[gbxl\]z files and dirs after % sign" -set oldpkg "xx-2.0-i486-2" -set dir $::srcdir/fixtures/slackware/home -set files [split [exec bash -c "cd $dir && find . -mindepth 1 -maxdepth 1 \ - \\( -type d -printf '$oldpkg%%%P/\\n' \\) -o \ - \\( -type f -name '*.t\[bglx\]z' -printf '$oldpkg%%%P\\n' \\)"] "\n"] -assert_complete_dir $files "upgradepkg $oldpkg%" $dir $test - - -sync_after_int - - -teardown diff --git a/test/lib/completions/xhost.exp b/test/lib/completions/xhost.exp deleted file mode 100644 index 02aa4cb2..00000000 --- a/test/lib/completions/xhost.exp +++ /dev/null @@ -1,95 +0,0 @@ -proc setup {} { - assert_bash_exec {HOME=$TESTDIR} - save_env -} - - -proc teardown {} { - assert_env_unmodified -} - - -setup - - -set test "Tab should complete hostnames" -assert_complete [get_hosts] "xhost " $test - - -sync_after_int - - -set test "Tab should complete partial hostname" -# Build string list of hostnames, starting with the character of the first hostname -set hosts {} -set char "" -foreach h [get_hosts] { - if {$char == ""} {set char [string range $h 0 0]} - # Only append hostname if starting with $char - if {[string range $h 0 0] == "$char"} { - lappend hosts $h - } -} -assert_complete $hosts "xhost $char" $test -expect-cmd-minus "$char" - - -sync_after_int - - -set test "Tab should complete hostnames prefixed with +" -# Build string list of hostnames, prefixed with plus (+) -set hosts {} -foreach h [get_hosts] { - lappend hosts "+$h" -} -assert_complete $hosts "xhost \+" $test - - -sync_after_int - - -set test "Tab should complete partial hostname prefixed with +" - # Build string list of hostnames, starting with character of first host. -set hosts {} -foreach h [get_hosts] { - if {$char == ""} {set char [string range $h 0 0]} - # Only append hostname if starting with $char - if {[string range $h 0 0] == "$char"} { - lappend hosts "+$h" - } -} -assert_complete $hosts "xhost +$char" $test -expect-cmd-minus "\+$char" - - -sync_after_int - - -set test "Tab should complete hostnames prefixed with -" - # Build string list of hostnames, prefix with minus (-) -set hosts {} -foreach h [get_hosts] { - lappend hosts "-$h" -} -assert_complete $hosts "xhost -" $test - - -sync_after_int - - -set test "Tab should complete partial hostname prefixed with -" - # Build list of hostnames, starting with character of first host -set hosts {} -foreach h [get_hosts] { - if {$char == ""} {set char [string range $h 0 0]} - # Only append hostname if starting with $char - if {[string range $h 0 0] == "$char"} { - lappend hosts "-$h" - } -} -assert_complete $hosts "xhost -$char" $test -expect-cmd-minus "-$char" - - -sync_after_int - - -teardown diff --git a/test/lib/library.exp b/test/lib/library.exp deleted file mode 100644 index c90c927c..00000000 --- a/test/lib/library.exp +++ /dev/null @@ -1,970 +0,0 @@ -# Source `init.tcl' again to restore the `unknown' procedure -# NOTE: DejaGnu has an old `unknown' procedure which unfortunately disables -# tcl auto-loading. -source [file join [info library] init.tcl] -package require cmdline -package require textutil::string - - - -# Execute a bash command and make sure the exit status is successful. -# If not, output the error message. -# @param string $cmd Bash command line to execute. If empty string (""), the -# exit status of the previously executed bash command will be -# checked; specify `title' to adorn the error message. -# @param string $title (optional) Command title. If empty, `cmd' is used. -# @param string $prompt (optional) Bash prompt. Default is "/@" -# @param mixed $out (optional) Reference to (tcl) variable to hold output. -# If variable equals -1 (default) the bash command is expected -# to return no output. If variable equals 0, any output -# from the bash command is disregarded. -proc assert_bash_exec {{aCmd ""} {title ""} {prompt /@} {out -1}} { - if {$out != 0 && $out != -1} {upvar $out results} - if {[string length $aCmd] != 0} { - send "$aCmd\r" - expect -ex "$aCmd\r\n" - } - if {[string length $title] == 0} {set title $aCmd} - expect -ex $prompt - set results $expect_out(buffer); # Catch output - # Remove $prompt suffix from output - set results [ - string range $results 0 [ - expr [string length $results] - [string length $prompt] - 1 - ] - ] - if {$out == -1 && [string length $results] > 0} { - fail "ERROR Unexpected output from bash command \"$title\"" - } - - set cmd "echo $?" - send "$cmd\r" - expect { - -ex "$cmd\r\n0\r\n$prompt" {} - $prompt {fail "ERROR executing bash command \"$title\""} - } -} - - -# Test `type ...' in bash -# Indicate "unsupported" if `type' exits with error status. -# @param string $command Command to locate -proc assert_bash_type {command} { - set test "$command should be available in bash" - set cmd "type $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; unsupported "$test" } - } - expect "/@" - return $result -} - - -# Make sure the expected list matches the real list, as returned by executing -# the specified bash command. -# Specify `-sort' if the real list is sorted. -# @param list $expected Expected list items -# @param string $cmd Bash command to execute in order to generate real list -# items -# @param string $test Test title. Becomes "$cmd should show expected output" -# if empty string. -# @param list $args Options: -# -sort Compare list sorted. Default is unsorted -# -prompt Bash prompt. Default is `/@' -# -chunk-size N Compare list N items at a time. Default -# is 20. -proc assert_bash_list {expected cmd test {args {}}} { - array set arg [::cmdline::getoptions args { - {sort "compare list sorted"} - {prompt.arg /@ "bash prompt"} - {chunk-size.arg 20 "compare N list items at a time"} - }] - set prompt $arg(prompt) - if {$test == ""} {set test "$cmd should show expected output"} - if {[llength $expected] == 0} { - assert_no_output $cmd $test $prompt - } else { - send "$cmd\r" - expect -ex "$cmd\r\n" - if {$arg(sort)} {set bash_sort "-bash-sort"} {set bash_sort ""} - if {[ - eval match_items \$expected $bash_sort -chunk-size \ - \$arg(chunk-size) -end-newline -end-prompt \ - -prompt \$prompt - ]} { - pass "$test" - } else { - fail "$test" - } - } -} - - -# Make sure the expected list matches the real list, as returned by executing -# the specified bash command within the specified directory. -# Specify `-sort' if the real list is sorted. -# @param list $expected Expected list items -# @param string $cmd Bash command to generate real list items -# @param string $dir Directory to execute $cmd within -# @param string $test Test title. Becomes "$cmd should show expected output" -# if empty string. -# @param list $args Options: -# -sort Compare list sorted. Default is unsorted -# -prompt Bash prompt. Default is `/@' -# -chunk-size N Compare list N items at a time. Default -# is 20. -proc assert_bash_list_dir {expected cmd dir test {args {}}} { - array set arg [::cmdline::getoptions args { - {sort "compare list sorted"} - {prompt.arg "/@" "bash prompt"} - {chunk-size.arg 20 "compare N list items at a time"} - }] - set prompt $arg(prompt) - if {$arg(sort)} {set arg_sort "-sort"} else {set arg_sort ""} - assert_bash_exec "cd $dir" "" $prompt - assert_bash_list $expected $cmd $test $arg_sort \ - -chunk-size $arg(chunk-size) -prompt $prompt - sync_after_int $prompt - assert_bash_exec {cd "$TESTDIR"} -} - - -# Make sure the expected items are returned by TAB-completing the specified -# 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 " -# -# @param list $expected Expected completions. -# @param string $cmd Command given to generate items -# @param string $test Test title -# @param list $args Options: -# -prompt PROMPT Bash prompt. Default is `/@' -# -chunk-size CHUNK-SIZE Compare list CHUNK-SIZE items at -# a time. Default is 20. -# -nospace Don't expect space character to be output after completion match. -# Valid only if a single completion is expected. -# -ltrim-colon-completions Left-trim completions with cword containing -# colon (:) -# -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 {}}} { - set args_orig $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-minus.arg "" "Expect cmd minus DWORD after prompt"} - }] - if {[llength $expected] == 0} { - assert_no_complete $cmd $test - } elseif {[llength $expected] == 1} { - eval assert_complete_one \$expected \$cmd \$test $args_orig - } else { - eval assert_complete_many \$expected \$cmd \$test $args_orig - } -} - - -# 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-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-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" - set trimmed false - if {$arg(ltrim-colon-completions)} { - # If partial contains colon (:), remove partial from begin of items - set trimmed [_ltrim_colon_completions $cmd expected cword] - } - if {! $trimmed} { - 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] - } - # Remove second word from beginning of $expected - if {[string first $cur $expected] == 0} { - set expected [list [string range $expected [string length $cur] end]] - } - } - - if {$arg(nospace)} {set endspace ""} else {set endspace "-end-space"} - if {[ - eval match_items \$expected -bash-sort -chunk-size \ - \$arg(chunk-size) $endspace -prompt \$prompt - ]} { - pass "$test" - } else { - fail "$test" - } -} - - -# @param string $cmd Command to remove current-word-to-complete from. -# @param string $dword (optional) Manually specify current-word-to-complete, -# i.e. word to remove from $cmd. If empty string (default), -# `_remove_cword_from_cmd' autodetects if the last argument is the -# current-word-to-complete by checking if $cmd doesn't end with whitespace. -# Specifying `dword' is only necessary if this autodetection fails, e.g. -# when the last whitespace is escaped or quoted, e.g. "finger foo\ " or -# "finger 'foo " -# @param string $common (optional) Common prefix of expected completions. -# @return string Command with current-word-to-complete removed -proc _remove_cword_from_cmd {cmd {dword ""} {common ""}} { - set cmd2 $cmd - # Is $dword specified? - if {[string length $dword] > 0} { - # Remove $dword from end of $cmd - if {[string last $dword $cmd] == [string length $cmd] - [string length $dword]} { - set cmd2 [string range $cmd 0 [expr [string last $dword $cmd] - 1]] - } - } else { - # No, $dword not specified; - # Check if last argument is really a word-to-complete, i.e. - # doesn't end with whitespace. - # NOTE: This check fails if trailing whitespace is escaped or quoted, - # e.g. "finger foo\ " or "finger 'foo ". Specify parameter - # $dword in those cases. - # Is last char whitespace? - if {! [string is space [string range $cmd end end]]} { - # No, last char isn't whitespace; - set cmds [split $cmd] - # 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 " " - } - } - } - return $cmd2 -} - - -# Escape regexp special characters -proc _escape_regexp_chars {var} { - upvar $var str - regsub -all {([\^$+*?.|(){}[\]\\])} $str {\\\1} str -} - -# Make sure any completions are returned -proc assert_complete_any {cmd {test ""} {prompt /@}} { - if {$test == ""} {set test "$cmd should show completions"} - send "$cmd\t" - expect -ex "$cmd" - _escape_regexp_chars cmd - expect { - -timeout 1 - # Match completions, multiple words - # NOTE: The `\S*' (zero or more non-whitespace characters) matches a - # longest common prefix of the completions shown. - # E.g. `fmt -' becomes `fmt --' (two dashes) when completing - -re "^\r\n.*$prompt$cmd\\S*$" { pass "$test" } - timeout { - expect { - # Match completion, single word. This word is shown on the - # same line as the command. - -re "^\\S* $" { pass "$test" } - # Try matching multiple words again, with new timeout - -re "^\r\n.*$prompt$cmd\\S*$" { pass "$test" } - } - } - -re $prompt { unresolved "$test at prompt" } - eof { unresolved "eof" } - } -} - - -# Make sure the expected files are returned by TAB-completing the specified -# command in the specified subdirectory. Be prepared to filter out OLDPWD -# changes when calling assert_env_unmodified() after using this procedure. -# @param list $expected -# @param string $cmd Command given to generate items -# @param string $dir Subdirectory to attempt completion in. The directory must be relative from the $TESTDIR and without a trailing slash. E.g. `fixtures/evince' -# @param string $test Test title -# @param list $args See: assert_complete() -# @result boolean True if successful, False if not -proc assert_complete_dir {expected cmd dir {test ""} {args {}}} { - set prompt "/@" - assert_bash_exec "cd $dir" "" $prompt - eval assert_complete \$expected \$cmd \$test $args - sync_after_int $prompt - assert_bash_exec {cd "$TESTDIR"} -} - - - -# 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 -proc _ltrim_colon_completions {cmd items dword} { - 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, - # and COMP_WORDBREAKS contains a colon - if { - [string first : $cur] > -1 && [string first ":" $::COMP_WORDBREAKS] > -1 - } { - set dword_out $cur - for {set i 0} {$i < [llength $items_out]} {incr i} { - set item [lindex $items_out $i] - if {[string first $cur $item] == 0} { - # Strip colon-prefix - lset items_out $i [string range $item [string length $cur] end] - } - } - return true - } - return false -} - - -# Make sure the bash environment hasn't changed between now and the last call -# to `save_env()'. -# @param string $sed Sed commands to preprocess diff output. -# Example calls: -# -# # Replace `COMP_PATH=.*' with `COMP_PATH=PATH' -# assert_env_unmodified {s/COMP_PATH=.*/COMP_PATH=PATH/} -# -# # Remove lines containing `OLDPWD=' -# assert_env_unmodified {/OLDPWD=/d} -# -# @param string $file Filename to generate environment save file from. See -# `gen_env_filename()'. -# @param string $diff Expected diff output (after being processed by $sed) -# @see save_env() -proc assert_env_unmodified {{sed ""} {file ""} {diff ""}} { - set test "Environment should not be modified" - _save_env [gen_env_filename $file 2] - - # Prepare sed script - - # Escape special bash characters ("\) - regsub -all {([\"\\])} $sed {\\\1} sed; #"# (fix Vim syntax highlighting) - # Escape newlines - regsub -all {\n} [string trim $sed] "\r\n" sed - - # Prepare diff script - - # If diff is filled, escape newlines and make sure it ends with a newline - if {[string length [string trim $diff]]} { - regsub -all {\n} [string trim $diff] "\r\n" diff - append diff "\r\n" - } else { - set diff "" - } - - # Execute diff - - # NOTE: The dummy argument 'LAST-ARG' sets bash variable $_ (last argument) to - # 'LAST-ARG' so that $_ doesn't mess up the diff (as it would if $_ - # was the (possibly multi-lined) sed script). - set cmd "diff_env \"[gen_env_filename $file 1]\" \"[gen_env_filename $file 2]\" \"$sed\" LAST-ARG" - send "$cmd\r" - expect "LAST-ARG\r\n" - - expect { - -re "^$diff[wd]@$" { pass "$test" } - -re [wd]@ { - fail "$test" - - # Show diff to user - - set diff $expect_out(buffer) - # Remove possible `\r\n[wd]@' from end of diff - if {[string last "\r\n[wd]@" $diff] == [string length $diff] - [string length "\r\n[wd]@"]} { - set diff [string range $diff 0 [expr [string last "\r\n[wd]@" $diff] - 1]] - } - send_user $diff; - } - } -} - - -# Check that no completion is attempted on a certain command. -# Params: -# @cmd The command to attempt to complete. -# @test Optional parameter with test name. -proc assert_no_complete {{cmd} {test ""}} { - if {[string length $test] == 0} { - set test "$cmd shouldn't complete" - } - - send "$cmd\t" - expect -ex "$cmd" - - # We can't anchor on $, simulate typing a magical string instead. - set endguard "Magic End Guard" - send "$endguard" - expect { - -re "^$endguard$" { pass "$test" } - default { fail "$test" } - timeout { fail "$test" } - } -} - - -# 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, and the command is available. -# @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 {[assert_bash_type $command] - && [assert_install_completion_for $command]} { - if {[string length $file] == 0} { - set file "$::srcdir/lib/completions/$command.exp" - } - source $file - } else { - untested $command - } -} - - -# Sort list. -# `exec sort' is used instead of `lsort' to achieve exactly the -# same sort order as in bash. -# @param list $items -# @return list Sort list -proc bash_sort {items} { - return [split [exec sort << [join $items "\n"]] "\n"] -} - - -# 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 {{cword ''}} { - assert_bash_exec "_known_hosts_real '$cword'; echo_array COMPREPLY" \ - {} /@ result - return $result -} - - -# Get hostnames -# @param list $args Options: -# -unsorted Do not sort unique. Default is sort unique. -# @return list Hostnames -# @see get_known_hosts() -proc get_hosts {{args {}}} { - array set arg [::cmdline::getoptions args { - {unsorted "do not sort unique"} - }] - set sort "| sort -u" - if {$arg(unsorted)} {set sort ""} - set hosts [exec bash -c "compgen -A hostname $sort"] - # NOTE: Circumventing var `avahi_hosts' and appending directly to `hosts' - # causes an empty element to be inserted in `hosts'. - # -- FVu, Fri Jul 17 23:11:46 CEST 2009 - set avahi_hosts [get_hosts_avahi] - if {[llength $avahi_hosts] > 0} { - lappend hosts $avahi_hosts - } - return $hosts -} - - -# Get hostnames according to avahi -# @return list Hostnames -proc get_hosts_avahi {} { - # Retrieving hosts is successful? - if { [catch {exec bash -c { - type avahi-browse >&/dev/null \ - && avahi-browse -cpr _workstation._tcp 2>/dev/null | command grep ^= | cut -d\; -f7 | sort -u - }} hosts] } { - # No, retrieving hosts yields error; - # Reset hosts - set hosts {} - } - return $hosts -} - - -# Initialize tcl globals with bash variables -proc init_tcl_bash_globals {} { - global BASH_VERSINFO BASH_VERSION COMP_WORDBREAKS LC_CTYPE - assert_bash_exec {printf "%s" "$COMP_WORDBREAKS"} {} /@ COMP_WORDBREAKS - assert_bash_exec {printf "%s " "${BASH_VERSINFO[@]}"} "" /@ BASH_VERSINFO - set BASH_VERSINFO [eval list $BASH_VERSINFO] - assert_bash_exec {printf "%s" "$BASH_VERSION"} "" /@ BASH_VERSION - assert_bash_exec {printf "%s" "$TESTDIR"} "" /@ TESTDIR - assert_bash_exec {eval $(locale); printf "%s" "$LC_CTYPE"} "" /@ LC_CTYPE -} - - -# Try installing completion for the specified command. -# @param string $command Command to install completion for. -# @return boolean True (1) if completion is installed, False (0) if not. -proc assert_install_completion_for {command} { - set test "$command should have completion installed in bash" - set cmd "__load_completion $command ; 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 -} - - -# Detect if test suite is running under Cygwin/Windows -proc is_cygwin {} { - expr {[string first [string tolower [exec uname -s]] cygwin] >= 0} -} - - -# Expect items, a limited number (20) at a time. -# Break items into chunks because `expect' seems to have a limited buffer size -# @param list $items Expected list items -# @param list $args Options: -# -bash-sort Compare list bash-sorted. Default is -# unsorted -# -prompt PROMPT Bash prompt. Default is `/@' -# -chunk-size CHUNK-SIZE Compare list CHUNK-SIZE items at -# a time. Default is 20. -# -end-newline Expect newline after last item. -# Default is not. -# -end-prompt Expect prompt after last item. -# Default is not. -# -end-space Expect single space after last item. -# Default is not. Valid only if -# `end-newline' not set. -# @result boolean True if successful, False if not -proc match_items {items {args {}}} { - array set arg [::cmdline::getoptions args { - {bash-sort "compare list sorted"} - {prompt.arg "/@" "bash prompt"} - {chunk-size.arg 20 "compare N list items at a time"} - {end-newline "expect newline after last item"} - {end-prompt "expect prompt after last item"} - {end-space "expect space ater last item"} - }] - set prompt $arg(prompt) - set size $arg(chunk-size) - if {$arg(bash-sort)} {set items [bash_sort $items]} - set result false - for {set i 0} {$i < [llength $items]} {set i [expr {$i + $size}]} { - # For chunks > 1, allow leading whitespace - if {$i > $size} { set expected "\\s*" } else { set expected "" } - for {set j 0} {$j < $size && $i + $j < [llength $items]} {incr j} { - set item "[lindex $items [expr {$i + $j}]]" - _escape_regexp_chars item - append expected $item - if {[llength $items] > 1} {append expected {\s+}} - } - if {[llength $items] == 1} { - if {$arg(end-prompt)} {set end $prompt} {set end ""} - # Both trailing space and newline are specified? - if {$arg(end-newline) && $arg(end-space)} { - # Indicate both trailing space or newline are ok - set expected2 "|^$expected $end$"; # Include space - append expected "\r\n$end"; # Include newline - } else { - if {$arg(end-newline)} {append expected "\r\n$end"} - if {$arg(end-space)} {append expected " $end"} - set expected2 "" - } - expect { - -re "^$expected$$expected2" { set result true } - -re "^$prompt$" {set result false; break } - default { set result false; break } - timeout { set result false; break } - } - } 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 { - -re "^$expected$end" { set result true } - default { set result false; break } - timeout { set result false; break } - } - } - } - return $result -} - - -# Generate filename to save environment to. -# @param string $file File-basename to save environment to. If the file has a -# `.exp' suffix, it is removed. E.g.: -# - "file.exp" becomes "file.env1~" -# - "" becomes "env.env1~" -# - "filename" becomes "filename.env1~" -# The file will be stored in the $TESTDIR/tmp directory. -# @param integer $seq Sequence number. Must be either 1 or 2. -proc gen_env_filename {{file ""} {seq 1}} { - if {[string length $file] == 0} { - set file "env" - } else { - # Remove possible directories - set file [file tail $file] - # Remove possible '.exp' suffix from filename - if {[string last ".exp" $file] == [string length $file] - [string length ".exp"]} { - set file [string range $file 0 [expr [string last ".exp" $file] - 1]] - } - } - return "\$TESTDIR/tmp/$file.env$seq~" -} - - -# Save the environment for later comparison -# @param string $file Filename to generate environment save file from. See -# `gen_env_filename()'. -proc save_env {{file ""}} { - _save_env [gen_env_filename $file 1] -} - - -# Save the environment for later comparison -# @param string File to save the environment to. Default is "$TESTDIR/tmp/env1~". -# @see assert_env_unmodified() -proc _save_env {{file ""}} { - assert_bash_exec "{ (set -o posix ; set); declare -F; shopt -p; set -o; } > \"$file\"" -} - - -# Source bash_completion package -proc source_bash_completion {} { - assert_bash_exec {source $(cd "$SRCDIR/.."; pwd)/bash_completion} -} - - -# Split line into words, disregarding backslash escapes (e.g. \b (backspace), -# \g (bell)), but taking backslashed spaces into account. -# Aimed for simulating bash word splitting. -# Example usage: -# -# % set a {f cd\ \be} -# % split_words $a -# f {cd\ \be} -# -# @param string Line to split -# @return list Words -proc split_words_bash {line} { - set words {} - set glue false - foreach part [split $line] { - set glue_next false - # Does `part' end with a backslash (\)? - if {[string last "\\" $part] == [string length $part] - [string length "\\"]} { - # Remove end backslash - set part [string range $part 0 [expr [string length $part] - [string length "\\"] - 1]] - # Indicate glue on next run - set glue_next true - } - # Must `part' be appended to latest word (= glue)? - if {[llength $words] > 0 && [string is true $glue]} { - # Yes, join `part' to latest word; - set zz [lindex $words [expr [llength $words] - 1]] - # Separate glue with backslash-space (\ ); - lset words [expr [llength $words] - 1] "$zz\\ $part" - } else { - # No, don't append word to latest word; - # Append `part' as separate word - lappend words $part - } - set glue $glue_next - } - return $words -} - - -# 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 - # Uniquify the list, that's what completion does too. - set list [lsort -unique $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 env srcdirabs - set TESTDIR [pwd] - set srcdirabs [file normalize $::srcdir]; # Absolute srcdir - # If `--tool_exec' option not specified, use "bash" - if {! [info exists TOOL_EXECUTABLE]} {set TOOL_EXECUTABLE bash} - set env(SRCDIR) $::srcdir - set env(SRCDIRABS) $::srcdirabs - - # PS1, INPUTRC, TERM and stty columns must be initialized - # *before* starting bash to take proper effect. - - # Set fixed prompt `/@' - set env(PS1) "/@" - # Configure readline - set env(INPUTRC) "$::srcdir/config/inputrc" - # Avoid escape junk at beginning of line from readline, - # see e.g. http://bugs.gentoo.org/246091 - set env(TERM) "dumb" - # Ensure enough columns so expect doesn't have to care about line breaks - set stty_init "columns 150" - - exp_spawn $TOOL_EXECUTABLE --norc - assert_bash_exec {} "$TOOL_EXECUTABLE --norc" - assert_bash_exec "source $::srcdir/config/bashrc" -} - - -# Redirect xtrace output to a file. -# -# 'set -x' can be very useful for debugging but by default it writes to -# stderr. -# -# This function uses file descriptor 6. This will break if any completion -# tries to use the same descriptor. -proc init_bash_xtrace {{fname xtrace.log}} { - verbose "Enabling bash xtrace output to '$fname'" - assert_bash_exec "exec 6>'$fname'" - assert_bash_exec "BASH_XTRACEFD=6" - assert_bash_exec "set -o xtrace" -} - - -# Setup test environment -# -# Common initialization for unit and completion tests. -proc start_interactive_test {} { - start_bash - source_bash_completion - init_tcl_bash_globals - - global OPT_BASH_XTRACE - if {[info exists OPT_BASH_XTRACE]} { - init_bash_xtrace - } - global OPT_BUFFER_SIZE - if {![info exists OPT_BUFFER_SIZE]} { - set OPT_BUFFER_SIZE 20000 - } - verbose "Changing default expect match buffer size to $OPT_BUFFER_SIZE" - match_max $OPT_BUFFER_SIZE - global OPT_TIMEOUT - if {[info exists OPT_TIMEOUT]} { - global timeout - verbose "Changing default expect timeout from $timeout to $OPT_TIMEOUT" - set timeout $OPT_TIMEOUT - } -} - - -# Interrupt completion and sync with prompt. -# Send signals QUIT & INT. -# @param string $prompt (optional) Bash prompt. Default is "/@" -proc sync_after_int {{prompt /@}} { - set test "Sync after INT" - sleep .1 - send \031\003; # QUIT/INT - # Wait to allow bash to become ready - # See also: http://lists.alioth.debian.org/pipermail/bash-completion-devel/ - # 2010-February/002566.html - sleep .1 - # NOTE: Regexp `.*' causes `expect' to discard previous unknown output. - # This is necessary if a completion doesn't match expectations. - # For instance with `filetype_xspec' completion (e.g. `kdvi') if - # one expects `.txt' as a completion (wrong, because it isn't - # there), the unmatched completions need to be cleaned up. - expect -re ".*$prompt$" -} - - -proc sync_after_tab {} { - # NOTE: Wait in case completion returns nothing - because `units' isn't - # installed, so that "^$cdm.*$" doesn't match too early - before - # comp_install has finished - sleep .4 -} - - -# Return current working directory with `TESTDIR' stripped -# @return string Working directory. E.g. /, or /fixtures/ -proc wd {} { - global TESTDIR - # Remove `$TESTDIR' prefix from current working directory - set wd [string replace [pwd] 0 [expr [string length $TESTDIR] - 1]]/ -} diff --git a/test/lib/library.sh b/test/lib/library.sh deleted file mode 100644 index ed5a85d4..00000000 --- a/test/lib/library.sh +++ /dev/null @@ -1,38 +0,0 @@ -# Bash library for bash-completion DejaGnu testsuite - - -# @param $1 Char to add to $COMP_WORDBREAKS -add_comp_wordbreak_char() { - [[ "${COMP_WORDBREAKS//[^$1]}" ]] || COMP_WORDBREAKS+=$1 -} # add_comp_wordbreak_char() - - -# Diff environment files to detect if environment is unmodified -# @param $1 File 1 -# @param $2 File 2 -# @param $3 Additional sed script -diff_env() { - diff "$1" "$2" | sed -e " -# Remove diff line indicators - /^[0-9,]\{1,\}[acd]/d -# Remove diff block separators - /---/d -# Remove underscore variable - /[<>] _=/d -# Remove PPID bash variable - /[<>] PPID=/d -# Remove BASH_REMATCH bash variable - /[<>] BASH_REMATCH=/d -# Remove functions starting with underscore - /[<>] declare -f _/d - $3" -} # diff_env() - - -# Output array elements, sorted and separated by newline -# Unset variable after outputting. -# @param $1 Name of array variable to process -echo_array() { - local name=$1[@] - printf "%s\n" "${!name}" | sort -} # echo_array() diff --git a/test/lib/unit.exp b/test/lib/unit.exp deleted file mode 100644 index e113e1b5..00000000 --- a/test/lib/unit.exp +++ /dev/null @@ -1,25 +0,0 @@ -source $::srcdir/lib/library.exp - - -proc unit_exit {} { - # Exit bash - send "\rexit\r" -} - - -proc unit_init {test_file_name} { - # Call unit_start() only once - if {! [info exists ::BASH_VERSINFO]} { - unit_start - } -} - - -proc unit_start {} { - start_interactive_test -} - - -proc unit_version {} { - puts "$::TESTDIR, bash-$::BASH_VERSION" -} diff --git a/test/requirements-dev.txt b/test/requirements-dev.txt new file mode 100644 index 00000000..f34f10fb --- /dev/null +++ b/test/requirements-dev.txt @@ -0,0 +1,4 @@ +# Python >= 3.6.1 required here +-r requirements.txt +black==19.10b0 +pre-commit>=2.4.0 diff --git a/test/requirements.txt b/test/requirements.txt index 70d77d02..df56f4e0 100644 --- a/test/requirements.txt +++ b/test/requirements.txt @@ -1,4 +1,4 @@ -black>=19.10b0;python_version>"3.6" +# Python >= 3.4 required here pexpect>=4 pytest>=3.6 pytest-xdist diff --git a/test/run b/test/run deleted file mode 100755 index 6180bbbf..00000000 --- a/test/run +++ /dev/null @@ -1,67 +0,0 @@ -#!/bin/bash - - -# Print some helpful messages. -usage() { - echo "Run bash-completion tests" - echo - echo "The 'tool' is determined automatically from filenames." - echo "Unrecognized options are passed through to dejagnu by default." - echo - echo "Interesting options:" - echo " --tool_exec= Test against a different bash executable." - echo " --buffer-size Change expect match buffer size from our default of 20000 bytes." - echo " --debug Create a dbg.log in the test directory with detailed expect match information." - echo " --timeout Change expect timeout from the default of 10 seconds." - echo " --debug-xtrace Create an xtrace.log in the test directory with set -x output." - echo - echo "Example run: ./run unit/_get_cword.exp unit/compgen.exp" -} - - -# Try to set the tool variable; or fail if trying to set different values. -set_tool() { - if [[ $tool ]]; then - if [[ $tool != $1 ]]; then - echo "Tool spec mismatch ('$tool' and '$1'). See --usage." - exit 1 - fi - else - tool=$1 - fi -} - - -cd "$(dirname "${BASH_SOURCE[0]}")" - - -# Loop over the arguments. -args=() -while [[ $# > 0 ]]; do - case "$1" in - --help|--usage) usage; exit 1;; - --buffer-size) shift; buffer_size=$1;; - --buffer-size=*) buffer_size=${1/--buffer-size=};; - --debug-xtrace) args+=(OPT_BASH_XTRACE=1);; - --timeout) shift; timeout=$1;; - --timeout=*) timeout=${1/--timeout=};; - --tool=*) set_tool "${1#/--tool=}";; - --tool) shift; set_tool "$1";; - completion/*.exp|*/completion/*.exp|unit/*.exp|*/unit/*.exp) - arg=${1%/*} - set_tool "${arg##*/}" - args+=("${1##*/}") - ;; - *) args+=("$1") - esac - shift -done - -[[ -n $buffer_size ]] && args+=("OPT_BUFFER_SIZE=$buffer_size") -[[ -n $timeout ]] && args+=("OPT_TIMEOUT=$timeout") -[[ -z $tool ]] && { echo "Must specify tool somehow"; exit 1; } - -runtest --outdir log --tool $tool "${args[@]}" -rc=$? -[[ $rc -ne 0 && -n "$CI" ]] && cat log/$tool.log -exit $rc diff --git a/test/run-shellcheck b/test/run-shellcheck deleted file mode 100755 index cae7b809..00000000 --- a/test/run-shellcheck +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -eu -cd "$(dirname $0)/.." -exec docker run --network none -tv "$PWD:/mnt:ro" \ - koalaman/shellcheck -S error "$@" diff --git a/test/runCompletion b/test/runCompletion deleted file mode 100755 index 38a82622..00000000 --- a/test/runCompletion +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -# NOTE: I tried setting up bash_completion_lib within ./lib files, but DejaGnu -# isn't initialized at that point (i.e. output of `expect' is shown on -# stdout - `open_logs' hasn't run yet?). And running code from a library -# file isn't probably a good idea either. -exec "${bashcomp_bash:-$BASH}" \ - "$(dirname "${BASH_SOURCE[0]}")/run" --tool completion $* diff --git a/test/runInstall b/test/runInstall deleted file mode 100755 index 87aea589..00000000 --- a/test/runInstall +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -# NOTE: I tried setting up bash_completion_lib within ./lib files, but DejaGnu -# isn't initialized at that point (i.e. output of `expect' is shown on -# stdout - `open_logs' hasn't run yet?). And running code from a library -# file isn't probably a good idea either. -exec runtest --outdir log --tool install $* diff --git a/test/runLint b/test/runLint index a84c46d8..95c3887e 100755 --- a/test/runLint +++ b/test/runLint @@ -1,10 +1,10 @@ -#!/bin/bash +#!/bin/bash -u gitgrep() { - local out=$(git grep -I -P -n "$1" | \ - grep -E '^(bash_completion|completions/|test/)' | \ - grep -Fv 'test/runLint') + local out=$(git grep -I -P -n "$1" | + grep -E '^(bash_completion|completions/|test/)' | + grep -Ev "^test/runLint\>${filter_out:+|$filter_out}") if [ -n "$out" ]; then printf '***** %s\n' "$2" printf '%s\n\n' "$out" @@ -12,9 +12,10 @@ gitgrep() } unset CDPATH -cd $(dirname "$0") ; cd .. +cd $(dirname "$0")/.. cmdstart='(^|[[:space:]]|\()' +filter_out= gitgrep $cmdstart"awk\b.*-F([[:space:]]|[[:space:]]*[\"'][^\"']{2,})" \ 'awk with -F char or -F ERE, use -Fchar instead (Solaris)' @@ -45,3 +46,6 @@ gitgrep '(?<!command)'$cmdstart'(grep|ls|sed)(\s|$)' \ 'invoke grep, ls, and sed through "command", e.g. "command grep"' gitgrep '<<<' 'herestrings use temp files, use some other way' + +filter_out='^(test/|bash_completion\.sh)' gitgrep ' \[ ' \ + 'use [[ ]] instead of [ ]' diff --git a/test/runUnit b/test/runUnit deleted file mode 100755 index 5df06227..00000000 --- a/test/runUnit +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/bash - -# NOTE: I tried setting up bash_completion_lib within ./lib files, but DejaGnu -# isn't initialized at that point (i.e. output of `expect' is shown on -# stdout - `open_logs' hasn't run yet?). And running code from a library -# file isn't probably a good idea either. -exec "${bashcomp_bash:-$BASH}" \ - "$(dirname "${BASH_SOURCE[0]}")/run" --tool unit $* diff --git a/test/setup.cfg b/test/setup.cfg index ef9c755e..6abd7d3a 100644 --- a/test/setup.cfg +++ b/test/setup.cfg @@ -7,3 +7,12 @@ markers = [mypy] python_version = 3.4 ignore_missing_imports = true + +[isort] +known_first_party = conftest +known_third_party = pexpect,pytest +profile = black +line_length = 79 + +[flake8] +extend-ignore = D202,E203,E501 diff --git a/test/t/Makefile.am b/test/t/Makefile.am index 0ce46b12..801841fb 100644 --- a/test/t/Makefile.am +++ b/test/t/Makefile.am @@ -45,6 +45,7 @@ EXTRA_DIST = \ test_bind.py \ test_bison.py \ test_bk.py \ + test_bmake.py \ test_brctl.py \ test_btdownloadcurses_py.py \ test_btdownloadgui_py.py \ @@ -132,6 +133,7 @@ EXTRA_DIST = \ test_dot.py \ test_dpkg.py \ test_dpkg_deb.py \ + test_dpkg_query.py \ test_dpkg_reconfigure.py \ test_dpkg_source.py \ test_dropdb.py \ @@ -249,6 +251,7 @@ EXTRA_DIST = \ test_invoke_rc_d.py \ test_ionice.py \ test_ip.py \ + test_ipcalc.py \ test_iperf.py \ test_ipmitool.py \ test_ipsec.py \ @@ -456,6 +459,7 @@ EXTRA_DIST = \ test_povray.py \ test_pr.py \ test_prelink.py \ + test_printenv.py \ test_protoc.py \ test_psql.py \ test_ptx.py \ @@ -523,9 +527,11 @@ EXTRA_DIST = \ test_sbcl.py \ test_sbcl_mt.py \ test_sbopkg.py \ + test_scp.py \ test_screen.py \ test_scrub.py \ test_sdptool.py \ + test_secret_tool.py \ test_sed.py \ test_seq.py \ test_service.py \ @@ -589,11 +595,13 @@ EXTRA_DIST = \ test_time.py \ test_timeout.py \ test_tipc.py \ + test_totem.py \ test_touch.py \ test_tox.py \ test_tr.py \ test_tracepath.py \ test_tshark.py \ + test_tsig_keygen.py \ test_tune2fs.py \ test_udevadm.py \ test_ulimit.py \ @@ -657,6 +665,7 @@ EXTRA_DIST = \ test_xdg_settings.py \ test_xfreerdp.py \ test_xgamma.py \ + test_xhost.py \ test_xm.py \ test_xmllint.py \ test_xmlwf.py \ diff --git a/test/t/conftest.py b/test/t/conftest.py index 20942e87..5c1603d5 100644 --- a/test/t/conftest.py +++ b/test/t/conftest.py @@ -3,18 +3,18 @@ import os import re import shlex import subprocess -from typing import Iterable, List, Optional, Tuple, Union +import time +from typing import Callable, Iterable, Iterator, List, Optional, Tuple import pexpect import pytest - PS1 = "/@" MAGIC_MARK = "__MaGiC-maRKz!__" def find_unique_completion_pair( - items: Iterable[str] + items: Iterable[str], ) -> Optional[Tuple[str, str]]: result = None bestscore = 0 @@ -56,10 +56,22 @@ def find_unique_completion_pair( @pytest.fixture(scope="class") -def part_full_user(bash: pexpect.spawn) -> Optional[Tuple[str, str]]: - res = ( - assert_bash_exec(bash, "compgen -u", want_output=True).strip().split() - ) +def output_sort_uniq(bash: pexpect.spawn) -> Callable[[str], List[str]]: + def _output_sort_uniq(command: str) -> List[str]: + return sorted( + set( # weed out possible duplicates + assert_bash_exec(bash, command, want_output=True).split() + ) + ) + + return _output_sort_uniq + + +@pytest.fixture(scope="class") +def part_full_user( + bash: pexpect.spawn, output_sort_uniq: Callable[[str], List[str]] +) -> Optional[Tuple[str, str]]: + res = output_sort_uniq("compgen -u") pair = find_unique_completion_pair(res) if not pair: pytest.skip("No suitable test user found") @@ -67,10 +79,10 @@ def part_full_user(bash: pexpect.spawn) -> Optional[Tuple[str, str]]: @pytest.fixture(scope="class") -def part_full_group(bash: pexpect.spawn) -> Optional[Tuple[str, str]]: - res = ( - assert_bash_exec(bash, "compgen -g", want_output=True).strip().split() - ) +def part_full_group( + bash: pexpect.spawn, output_sort_uniq: Callable[[str], List[str]] +) -> Optional[Tuple[str, str]]: + res = output_sort_uniq("compgen -g") pair = find_unique_completion_pair(res) if not pair: pytest.skip("No suitable test user found") @@ -78,6 +90,82 @@ def part_full_group(bash: pexpect.spawn) -> Optional[Tuple[str, str]]: @pytest.fixture(scope="class") +def hosts(bash: pexpect.spawn) -> List[str]: + output = assert_bash_exec(bash, "compgen -A hostname", want_output=True) + return sorted(set(output.split() + _avahi_hosts(bash))) + + +@pytest.fixture(scope="class") +def avahi_hosts(bash: pexpect.spawn) -> List[str]: + return _avahi_hosts(bash) + + +def _avahi_hosts(bash: pexpect.spawn) -> List[str]: + output = assert_bash_exec( + bash, + "! type avahi-browse &>/dev/null || " + "avahi-browse -cpr _workstation._tcp 2>/dev/null " + "| command grep ^= | cut -d';' -f7", + want_output=None, + ) + return sorted(set(output.split())) + + +@pytest.fixture(scope="class") +def known_hosts(bash: pexpect.spawn) -> List[str]: + output = assert_bash_exec( + bash, + '_known_hosts_real ""; ' + r'printf "%s\n" "${COMPREPLY[@]}"; unset COMPREPLY', + want_output=True, + ) + return sorted(set(output.split())) + + +@pytest.fixture(scope="class") +def user_home(bash: pexpect.spawn) -> Tuple[str, str]: + user = assert_bash_exec( + bash, 'id -un 2>/dev/null || echo "$USER"', want_output=True + ).strip() + home = assert_bash_exec(bash, 'echo "$HOME"', want_output=True).strip() + return (user, home) + + +def partialize( + bash: pexpect.spawn, items: Iterable[str] +) -> Tuple[str, List[str]]: + """ + Get list of items starting with the first char of first of items. + + Disregard items starting with a COMP_WORDBREAKS character + (e.g. a colon ~ IPv6 address), they are special cases requiring + special tests. + """ + first_char = None + comp_wordbreaks = assert_bash_exec( + bash, + 'printf "%s" "$COMP_WORDBREAKS"', + want_output=True, + want_newline=False, + ) + partial_items = [] + for item in sorted(items): + if first_char is None: + if item[0] not in comp_wordbreaks: + first_char = item[0] + partial_items.append(item) + elif item.startswith(first_char): + partial_items.append(item) + else: + break + if first_char is None: + pytest.skip("Could not generate partial items list from %s" % items) + # superfluous/dead code to assist mypy; pytest.skip always raises + assert first_char is not None + return first_char, partial_items + + +@pytest.fixture(scope="class") def bash(request) -> pexpect.spawn: logfile = None @@ -135,7 +223,7 @@ def bash(request) -> pexpect.spawn: skipif = marker.kwargs.get("skipif") if skipif: try: - assert_bash_exec(bash, skipif) + assert_bash_exec(bash, skipif, want_output=None) except AssertionError: pass else: @@ -144,7 +232,7 @@ def bash(request) -> pexpect.spawn: xfail = marker.kwargs.get("xfail") if xfail: try: - assert_bash_exec(bash, xfail) + assert_bash_exec(bash, xfail, want_output=None) except AssertionError: pass else: @@ -182,7 +270,7 @@ def bash(request) -> pexpect.spawn: logfile.close() -def is_testable(bash: pexpect.spawn, cmd: str) -> bool: +def is_testable(bash: pexpect.spawn, cmd: Optional[str]) -> bool: if not cmd: pytest.fail("Could not resolve name of command to test") return False @@ -214,8 +302,14 @@ def load_completion_for(bash: pexpect.spawn, cmd: str) -> bool: def assert_bash_exec( - bash: pexpect.spawn, cmd: str, want_output: bool = False, want_newline=True + bash: pexpect.spawn, + cmd: str, + want_output: Optional[bool] = False, + want_newline=True, ) -> str: + """ + :param want_output: if None, don't care if got output or not + """ # Send command bash.sendline(cmd) @@ -243,16 +337,17 @@ def assert_bash_exec( status, output, ) - if output: - assert want_output, ( - 'Unexpected output from "%s": exit status=%s, output="%s"' - % (cmd, status, output) - ) - else: - assert not want_output, ( - 'Expected output from "%s": exit status=%s, output="%s"' - % (cmd, status, output) - ) + if want_output is not None: + if output: + assert want_output, ( + 'Unexpected output from "%s": exit status=%s, output="%s"' + % (cmd, status, output) + ) + else: + assert not want_output, ( + 'Expected output from "%s": exit status=%s, output="%s"' + % (cmd, status, output) + ) return output @@ -293,76 +388,52 @@ def diff_env(before: List[str], after: List[str], ignore: str): assert not diff, "Environment should not be modified" -class CompletionResult: +class CompletionResult(Iterable[str]): """ Class to hold completion results. """ - def __init__(self, output: str, items: Optional[Iterable[str]] = None): + def __init__(self, output: Optional[str] = None): """ - When items are specified, they are used as the base for comparisons - provided by this class. When not, regular expressions are used instead. - This is because it is not always possible to unambiguously split a - completion output string into individual items, for example when the - items contain whitespace. - :param output: All completion output as-is. - :param items: Completions as individual items. Should be specified - only in cases where the completions are robustly known to be - exactly the specified ones. """ - self.output = output - self._items = None if items is None else sorted(items) + self.output = output or "" def endswith(self, suffix: str) -> bool: return self.output.endswith(suffix) - def __eq__(self, expected: Union[str, Iterable[str]]) -> bool: + def startswith(self, prefix: str) -> bool: + return self.output.startswith(prefix) + + def _items(self) -> List[str]: + return [x.strip() for x in self.output.strip().splitlines()] + + def __eq__(self, expected: object) -> bool: """ Returns True if completion contains expected items, and no others. Defining __eq__ this way is quite ugly, but facilitates concise testing code. """ - expiter = [expected] if isinstance(expected, str) else expected - if self._items is not None: - return self._items == expiter - return bool( - re.match( - r"^\s*" + r"\s+".join(re.escape(x) for x in expiter) + r"\s*$", - self.output, - ) - ) + if isinstance(expected, str): + expiter = [expected] # type: Iterable + elif not isinstance(expected, Iterable): + return False + else: + expiter = expected + return self._items() == expiter def __contains__(self, item: str) -> bool: - if self._items is not None: - return item in self._items - return bool( - re.search(r"(^|\s)%s(\s|$)" % re.escape(item), self.output) - ) + return item in self._items() - def __iter__(self) -> Iterable[str]: - """ - Note that iteration over items may not be accurate when items were not - specified to the constructor, if individual items in the output contain - whitespace. In those cases, it errs on the side of possibly returning - more items than there actually are, and intends to never return fewer. - """ - return iter( - self._items - if self._items is not None - else re.split(r" {2,}|\r\n", self.output.strip()) - ) + def __iter__(self) -> Iterator[str]: + return iter(self._items()) def __len__(self) -> int: - """ - Uses __iter__, see caveat in it. While possibly inaccurate, this is - good enough for truthiness checks. - """ - return len(list(iter(self))) + return len(self._items()) def __repr__(self) -> str: - return "<CompletionResult %s>" % list(self) + return "<CompletionResult %s>" % self._items() def assert_complete( @@ -371,7 +442,7 @@ def assert_complete( skipif = kwargs.get("skipif") if skipif: try: - assert_bash_exec(bash, skipif) + assert_bash_exec(bash, skipif, want_output=None) except AssertionError: pass else: @@ -379,7 +450,7 @@ def assert_complete( xfail = kwargs.get("xfail") if xfail: try: - assert_bash_exec(bash, xfail) + assert_bash_exec(bash, xfail, want_output=None) except AssertionError: pass else: @@ -393,57 +464,63 @@ def assert_complete( # Back up environment and apply new one assert_bash_exec( bash, - " ".join('%s%s="$%s"' % (env_prefix, k, k) for k in env.keys()), + " ".join('%s%s="${%s-}"' % (env_prefix, k, k) for k in env.keys()), ) assert_bash_exec( bash, "export %s" % " ".join("%s=%s" % (k, v) for k, v in env.items()), ) - bash.send(cmd + "\t") - bash.expect_exact(cmd) - bash.send(MAGIC_MARK) - got = bash.expect( - [ - # 0: multiple lines, result in .before - r"\r\n" + re.escape(PS1 + cmd) + ".*" + MAGIC_MARK, - # 1: no completion - r"^" + MAGIC_MARK, - # 2: on same line, result in .match - r"^([^\r]+)%s$" % MAGIC_MARK, - pexpect.EOF, - pexpect.TIMEOUT, - ] - ) - if got == 0: - output = bash.before - if output.endswith(MAGIC_MARK): - output = bash.before[: -len(MAGIC_MARK)] - result = CompletionResult(output) - elif got == 2: - output = bash.match.group(1) - result = CompletionResult(output, [shlex.split(cmd + output)[-1]]) - else: - # TODO: warn about EOF/TIMEOUT? - result = CompletionResult("", []) - bash.sendintr() - bash.expect_exact(PS1) - if env: - # Restore environment, and clean up backup - # TODO: Test with declare -p if a var was set, backup only if yes, and - # similarly restore only backed up vars. Should remove some need - # for ignore_env. - assert_bash_exec( - bash, - "export %s" - % " ".join('%s="$%s%s"' % (k, env_prefix, k) for k in env.keys()), - ) - assert_bash_exec( - bash, - "unset -v %s" - % " ".join("%s%s" % (env_prefix, k) for k in env.keys()), + try: + bash.send(cmd + "\t") + # Sleep a bit if requested, to avoid `.*` matching too early + time.sleep(kwargs.get("sleep_after_tab", 0)) + bash.expect_exact(cmd) + bash.send(MAGIC_MARK) + got = bash.expect( + [ + # 0: multiple lines, result in .before + r"\r\n" + re.escape(PS1 + cmd) + ".*" + re.escape(MAGIC_MARK), + # 1: no completion + r"^" + re.escape(MAGIC_MARK), + # 2: on same line, result in .match + r"^([^\r]+)%s$" % re.escape(MAGIC_MARK), + pexpect.EOF, + pexpect.TIMEOUT, + ] ) - if cwd: - assert_bash_exec(bash, "cd - >/dev/null") + if got == 0: + output = bash.before + if output.endswith(MAGIC_MARK): + output = bash.before[: -len(MAGIC_MARK)] + result = CompletionResult(output) + elif got == 2: + output = bash.match.group(1) + result = CompletionResult(output) + else: + # TODO: warn about EOF/TIMEOUT? + result = CompletionResult() + finally: + bash.sendintr() + bash.expect_exact(PS1) + if env: + # Restore environment, and clean up backup + # TODO: Test with declare -p if a var was set, backup only if yes, and + # similarly restore only backed up vars. Should remove some need + # for ignore_env. + assert_bash_exec( + bash, + "export %s" + % " ".join( + '%s="$%s%s"' % (k, env_prefix, k) for k in env.keys() + ), + ) + assert_bash_exec( + bash, + "unset -v %s" + % " ".join("%s%s" % (env_prefix, k) for k in env.keys()), + ) + if cwd: + assert_bash_exec(bash, "cd - >/dev/null") return result @@ -451,7 +528,7 @@ def assert_complete( def completion(request, bash: pexpect.spawn) -> CompletionResult: marker = request.node.get_closest_marker("complete") if not marker: - return CompletionResult("", []) + return CompletionResult() for pre_cmd in marker.kwargs.get("pre_cmds", []): assert_bash_exec(bash, pre_cmd) cmd = getattr(request.cls, "cmd", None) @@ -467,9 +544,61 @@ def completion(request, bash: pexpect.spawn) -> CompletionResult: ) % ((cmd,) * 2) if marker.kwargs.get("require_cmd") and not is_bash_type(bash, cmd): pytest.skip("Command not found") + + if "trail" in marker.kwargs: + return assert_complete_at_point( + bash, cmd=marker.args[0], trail=marker.kwargs["trail"] + ) + return assert_complete(bash, marker.args[0], **marker.kwargs) +def assert_complete_at_point( + bash: pexpect.spawn, cmd: str, trail: str +) -> CompletionResult: + # TODO: merge to assert_complete + fullcmd = "%s%s%s" % ( + cmd, + trail, + "\002" * len(trail), + ) # \002 = ^B = cursor left + bash.send(fullcmd + "\t") + bash.send(MAGIC_MARK) + bash.expect_exact(fullcmd.replace("\002", "\b")) + + got = bash.expect_exact( + [ + # 0: multiple lines, result in .before + PS1 + fullcmd.replace("\002", "\b"), + # 1: no completion + MAGIC_MARK, + pexpect.EOF, + pexpect.TIMEOUT, + ] + ) + if got == 0: + output = bash.before + result = CompletionResult(output) + + # At this point, something weird happens. For most test setups, as + # expected (pun intended!), MAGIC_MARK follows as is. But for some + # others (e.g. CentOS 6, Ubuntu 14 test containers), we get MAGIC_MARK + # one character a time, followed each time by trail and the corresponding + # number of \b's. Don't know why, but accept it until/if someone finds out. + # Or just be fine with it indefinitely, the visible and practical end + # result on a terminal is the same anyway. + repeat = "(%s%s)?" % (re.escape(trail), "\b" * len(trail)) + fullexpected = "".join( + "%s%s" % (re.escape(x), repeat) for x in MAGIC_MARK + ) + bash.expect(fullexpected) + else: + # TODO: warn about EOF/TIMEOUT? + result = CompletionResult() + + return result + + def in_container() -> bool: try: container = subprocess.check_output( diff --git a/test/t/test_2to3.py b/test/t/test_2to3.py index 030fb261..4bce44e6 100644 --- a/test/t/test_2to3.py +++ b/test/t/test_2to3.py @@ -6,6 +6,6 @@ class Test2to3: def test_1(self, completion): assert completion - @pytest.mark.complete("2to3 -", require_cmd=True) + @pytest.mark.complete("2to3 -", require_cmd=True, require_longopt=True) def test_2(self, completion): assert completion diff --git a/test/t/test_7z.py b/test/t/test_7z.py index c6e73890..d4308d95 100644 --- a/test/t/test_7z.py +++ b/test/t/test_7z.py @@ -8,12 +8,11 @@ class Test7z: @pytest.mark.complete("7z a ar -tzi") def test_2(self, completion): - assert completion == "-tzip" + assert completion == "p" - @pytest.mark.xfail # TODO: whitespace split issue @pytest.mark.complete(r"7z x -wa\ ", cwd="_filedir") def test_3(self, completion): - assert completion == r"-wa\ b/" + assert completion == "b/" assert not completion.endswith(" ") @pytest.mark.complete("7z x ", cwd="7z") diff --git a/test/t/test_alias.py b/test/t/test_alias.py index da9ecc33..cc592a8c 100644 --- a/test/t/test_alias.py +++ b/test/t/test_alias.py @@ -15,3 +15,7 @@ class TestAlias: def test_2(self, completion): assert completion == "foo='bar'" assert not completion.endswith(" ") + + @pytest.mark.complete("alias ", trail="foo") + def test_alias_at_point(self, completion): + assert completion == "bar foo".split() diff --git a/test/t/test_ant.py b/test/t/test_ant.py index b14beb94..94acea11 100644 --- a/test/t/test_ant.py +++ b/test/t/test_ant.py @@ -1,5 +1,7 @@ import pytest +from conftest import assert_bash_exec + @pytest.mark.bashcomp(ignore_env=r"^\+ANT_ARGS=") class TestAnt: @@ -18,8 +20,15 @@ class TestAnt: @pytest.mark.complete( "ant ", cwd="ant", env=dict(ANT_ARGS="'-f named-build.xml'") ) - def test_4(self, completion): - assert completion == "named-build" + def test_4(self, bash, completion): + output = assert_bash_exec(bash, "complete -p ant", want_output=True) + if "complete-ant-cmd.pl" in output: + # Some versions of complete-ant-cmd.pl don't treat ANT_ARGS right; + # in those cases we get the correct completion produced by _ant + # plus whatever complete-ant-cmd.pl was able to get from build.xml + assert "named-build" in completion + else: + assert completion == "named-build" @pytest.mark.complete("ant -l ") def test_5(self, completion): diff --git a/test/t/test_apt_cache.py b/test/t/test_apt_cache.py index a1c29cda..f9329f22 100644 --- a/test/t/test_apt_cache.py +++ b/test/t/test_apt_cache.py @@ -5,9 +5,13 @@ import pytest class TestAptCache: @pytest.mark.complete("apt-cache ") def test_1(self, completion): - assert completion + assert "search" in completion @pytest.mark.complete("apt-cache showsrc [", require_cmd=True) def test_2(self, completion): # Doesn't actually fail on grep errors, but takes a long time. assert not completion + + @pytest.mark.complete("apt-cache ", trail=" add foo") + def test_special_at_point(self, completion): + assert not completion diff --git a/test/t/test_apt_get.py b/test/t/test_apt_get.py index ccdff6cd..dc8299a9 100644 --- a/test/t/test_apt_get.py +++ b/test/t/test_apt_get.py @@ -9,4 +9,8 @@ class TestAptGet: @pytest.mark.complete("apt-get install ./", cwd="dpkg") def test_2(self, completion): - assert completion == "./bash-completion-test-subject.deb" + assert completion == "bash-completion-test-subject.deb" + + @pytest.mark.complete("apt-get build-dep ") + def test_build_dep_dirs(self, completion): + assert "dpkg/" in completion diff --git a/test/t/test_aptitude.py b/test/t/test_aptitude.py index c59c3580..29569f15 100644 --- a/test/t/test_aptitude.py +++ b/test/t/test_aptitude.py @@ -5,3 +5,19 @@ class TestAptitude: @pytest.mark.complete("aptitude ") def test_1(self, completion): assert completion + + @pytest.mark.complete("aptitude -", require_cmd=True) + def test_options(self, completion): + assert completion + + @pytest.mark.complete("aptitude --", require_cmd=True) + def test_long_options(self, completion): + assert completion + + @pytest.mark.complete("aptitude -u -") + def test_no_i_with_u(self, completion): + assert "-i" not in completion + + @pytest.mark.complete("aptitude -i -") + def test_no_u_with_i(self, completion): + assert "-u" not in completion diff --git a/test/t/test_arpspoof.py b/test/t/test_arpspoof.py index c8955f8d..74c09a43 100644 --- a/test/t/test_arpspoof.py +++ b/test/t/test_arpspoof.py @@ -2,6 +2,11 @@ import pytest class TestArpspoof: - @pytest.mark.complete("arpspoof -", require_cmd=True) + @pytest.mark.complete( + "arpspoof -", + require_cmd=True, + # May require privileges even for outputting the usage message + skipif="arpspoof 2>&1 | command grep -qF libnet_open_link", + ) def test_1(self, completion): assert completion diff --git a/test/t/test_bmake.py b/test/t/test_bmake.py new file mode 100644 index 00000000..bc885d31 --- /dev/null +++ b/test/t/test_bmake.py @@ -0,0 +1,7 @@ +import pytest + + +class TestBmake: + @pytest.mark.complete("bmake -", require_cmd=True) + def test_options(self, completion): + assert completion diff --git a/test/t/test_ccache.py b/test/t/test_ccache.py index 64620ef4..ef55d0d8 100644 --- a/test/t/test_ccache.py +++ b/test/t/test_ccache.py @@ -12,15 +12,15 @@ class TestCcache: @pytest.mark.complete("ccache stt") def test_3(self, completion): - assert "stty" in completion + assert completion == "y" or "stty" in completion @pytest.mark.complete("ccache --zero-stats stt") def test_4(self, completion): - assert "stty" in completion + assert completion == "y" or "stty" in completion @pytest.mark.complete("ccache --hel", require_cmd=True) def test_5(self, completion): - assert "--help" in completion + assert completion == "p" or "--help" in completion @pytest.mark.complete("ccache --zero-stats sh +") def test_6(self, completion): diff --git a/test/t/test_cd.py b/test/t/test_cd.py index fd532312..5b7789ae 100644 --- a/test/t/test_cd.py +++ b/test/t/test_cd.py @@ -9,7 +9,7 @@ class TestCd: @pytest.mark.complete("cd fo", env=dict(CDPATH="shared/default")) def test_2(self, completion): - assert completion == "foo.d/" + assert completion == "o.d/" @pytest.mark.complete("cd fo") def test_3(self, completion): @@ -20,3 +20,7 @@ class TestCd: ) def test_4(self, completion): assert not completion # No subdirs nor CDPATH + + @pytest.mark.complete("cd shared/default/", trail="foo") + def test_dir_at_point(self, completion): + assert completion == ["bar bar.d/", "foo.d/"] diff --git a/test/t/test_chown.py b/test/t/test_chown.py index 37221cfa..9643f3eb 100644 --- a/test/t/test_chown.py +++ b/test/t/test_chown.py @@ -2,7 +2,7 @@ import getpass import pytest -from conftest import assert_bash_exec, assert_complete +from conftest import assert_complete @pytest.mark.bashcomp( @@ -16,10 +16,8 @@ class TestChown: getpass.getuser() != "root", reason="Only root can chown to all users" ) @pytest.mark.complete("chown ") - def test_1(self, bash, completion): - users = sorted( - assert_bash_exec(bash, "compgen -A user", want_output=True).split() - ) + def test_1(self, bash, completion, output_sort_uniq): + users = output_sort_uniq("compgen -u") assert completion == users @pytest.mark.complete("chown foo: shared/default/") @@ -33,37 +31,39 @@ class TestChown: def test_4(self, bash, part_full_user): part, full = part_full_user completion = assert_complete(bash, "chown %s" % part) - assert completion == full + assert completion == full[len(part) :] assert completion.endswith(" ") def test_5(self, bash, part_full_user, part_full_group): _, user = part_full_user partgroup, fullgroup = part_full_group completion = assert_complete(bash, "chown %s:%s" % (user, partgroup)) - assert completion == "%s:%s" % (user, fullgroup) + assert completion == fullgroup[len(partgroup) :] assert completion.output.endswith(" ") def test_6(self, bash, part_full_group): part, full = part_full_group completion = assert_complete(bash, "chown dot.user:%s" % part) - assert completion == "dot.user:%s" % full + assert completion == full[len(part) :] assert completion.output.endswith(" ") - @pytest.mark.xfail # TODO check escaping, whitespace - def test_7(self, bash, part_full_group): - """Test preserving special chars in $prefix$partgroup<TAB>.""" - part, full = part_full_group - for prefix in ( + @pytest.mark.parametrize( + "prefix", + [ r"funky\ user:", "funky.user:", r"funky\.user:", r"fu\ nky.user:", r"f\ o\ o\.\bar:", r"foo\_b\ a\.r\ :", - ): - completion = assert_complete(bash, "chown %s%s" % (prefix, part)) - assert completion == "%s%s" % (prefix, full) - assert completion.output.endswith(" ") + ], + ) + def test_7(self, bash, part_full_group, prefix): + """Test preserving special chars in $prefix$partgroup<TAB>.""" + part, full = part_full_group + completion = assert_complete(bash, "chown %s%s" % (prefix, part)) + assert completion == full[len(part) :] + assert completion.output.endswith(" ") def test_8(self, bash, part_full_user, part_full_group): """Test giving up on degenerate cases instead of spewing junk.""" diff --git a/test/t/test_complete.py b/test/t/test_complete.py index 036f954e..7ff56b41 100644 --- a/test/t/test_complete.py +++ b/test/t/test_complete.py @@ -5,3 +5,7 @@ class TestComplete: @pytest.mark.complete("complete -") def test_1(self, completion): assert completion + + @pytest.mark.complete(r"\complete -") + def test_2(self, completion): + assert completion diff --git a/test/t/test_cpan2dist.py b/test/t/test_cpan2dist.py index f456c0ce..1ab5de13 100644 --- a/test/t/test_cpan2dist.py +++ b/test/t/test_cpan2dist.py @@ -2,6 +2,8 @@ import pytest class TestCpan2dist: - @pytest.mark.complete("cpan2dist -", require_cmd=True) + @pytest.mark.complete( + "cpan2dist -", require_cmd=True, require_longopt=True + ) def test_1(self, completion): assert completion diff --git a/test/t/test_cpio.py b/test/t/test_cpio.py index 1b9e37df..0b739663 100644 --- a/test/t/test_cpio.py +++ b/test/t/test_cpio.py @@ -1,7 +1,5 @@ import pytest -from conftest import assert_bash_exec - class TestCpio: @pytest.mark.complete("cpio --") @@ -9,10 +7,6 @@ class TestCpio: assert completion @pytest.mark.complete("cpio -R ") - def test_2(self, bash, completion): - users = sorted( - assert_bash_exec(bash, "compgen -A user", want_output=True) - .strip() - .splitlines() - ) - assert list(completion) == users + def test_2(self, bash, completion, output_sort_uniq): + users = output_sort_uniq("compgen -u") + assert completion == users diff --git a/test/t/test_cppcheck.py b/test/t/test_cppcheck.py index da770786..73e64f5e 100644 --- a/test/t/test_cppcheck.py +++ b/test/t/test_cppcheck.py @@ -20,12 +20,12 @@ class TestCppcheck: @pytest.mark.complete("cppcheck --enable=al") def test_5(self, completion): - assert completion == "--enable=all" + assert completion == "l" @pytest.mark.complete("cppcheck --enable=xx,styl") def test_6(self, completion): - assert completion == "--enable=xx,style" + assert completion == "e" @pytest.mark.complete("cppcheck --enable=xx,yy,styl") def test_7(self, completion): - assert completion == "--enable=xx,yy,style" + assert completion == "e" diff --git a/test/t/test_crontab.py b/test/t/test_crontab.py index 098fd9e0..a476694c 100644 --- a/test/t/test_crontab.py +++ b/test/t/test_crontab.py @@ -5,3 +5,12 @@ class TestCrontab: @pytest.mark.complete("crontab ") def test_1(self, completion): assert completion + + @pytest.mark.complete("crontab -l -") + def test_only_u_with_l(self, completion): + assert completion == "u" + + @pytest.mark.complete("crontab -r -") + def test_no_l_with_r(self, completion): + assert completion + assert "-l" not in completion diff --git a/test/t/test_curl.py b/test/t/test_curl.py index ebccca95..63e969f9 100644 --- a/test/t/test_curl.py +++ b/test/t/test_curl.py @@ -8,11 +8,11 @@ class TestCurl: @pytest.mark.complete("curl -o f", cwd="shared/default/foo.d") def test_2(self, completion): - assert completion == "foo" + assert completion == "oo" @pytest.mark.complete("curl -LRo f", cwd="shared/default/foo.d") def test_3(self, completion): - assert completion == "foo" + assert completion == "oo" @pytest.mark.complete("curl --o f") def test_4(self, completion): @@ -20,9 +20,9 @@ class TestCurl: @pytest.mark.complete("curl --data @", cwd="shared/default/foo.d") def test_data_atfile(self, completion): - assert completion == "@foo" + assert completion == "foo" @pytest.mark.complete("curl --data @foo.", cwd="shared/default") def test_data_atfile_dir(self, completion): - assert completion == "@foo.d/" + assert completion == "d/" assert not completion.endswith(" ") diff --git a/test/t/test_cvs.py b/test/t/test_cvs.py index ab7fead8..97361e9e 100644 --- a/test/t/test_cvs.py +++ b/test/t/test_cvs.py @@ -13,7 +13,7 @@ class TestCvs: @pytest.mark.complete("cvs diff foo/", cwd="cvs") def test_3(self, completion): - assert completion == "foo/bar" + assert completion == "bar" @pytest.mark.complete("cvs -", require_cmd=True) def test_4(self, completion): diff --git a/test/t/test_dd.py b/test/t/test_dd.py index be1829d3..e082faa9 100644 --- a/test/t/test_dd.py +++ b/test/t/test_dd.py @@ -14,4 +14,4 @@ class TestDd: @pytest.mark.complete("dd bs") def test_2(self, completion): - assert completion == "bs=" + assert completion == "=" diff --git a/test/t/test_dmypy.py b/test/t/test_dmypy.py index efaef7ca..4c031ddd 100644 --- a/test/t/test_dmypy.py +++ b/test/t/test_dmypy.py @@ -2,11 +2,13 @@ import pytest class TestDmypy: - @pytest.mark.complete("dmypy ", require_cmd=True) + @pytest.mark.complete( + "dmypy ", require_cmd=True, xfail="! dmypy --help &>/dev/null" + ) def test_commands(self, completion): assert "help" in completion assert not any("," in x for x in completion) - @pytest.mark.complete("dmypy -", require_cmd=True) + @pytest.mark.complete("dmypy -", require_cmd=True, require_longopt=True) def test_options(self, completion): assert "--help" in completion diff --git a/test/t/test_dnssec_keygen.py b/test/t/test_dnssec_keygen.py index d52e3af0..f8bd6fb1 100644 --- a/test/t/test_dnssec_keygen.py +++ b/test/t/test_dnssec_keygen.py @@ -13,7 +13,7 @@ class TestDnssecKeygen: @pytest.mark.complete("dnssec-keygen -a ") def test_2(self, completion): assert completion - assert "HMAC-MD5" in completion + assert any(x in completion for x in ("HMAC-MD5", "RSASHA1", "ED25519")) assert "|" not in completion assert not any(x.startswith("-") for x in completion) diff --git a/test/t/test_dpkg_deb.py b/test/t/test_dpkg_deb.py index c1ad8191..9be85eb6 100644 --- a/test/t/test_dpkg_deb.py +++ b/test/t/test_dpkg_deb.py @@ -6,3 +6,7 @@ class TestDpkgDeb: @pytest.mark.complete("dpkg-deb --c", require_cmd=True) def test_1(self, completion): assert completion + + @pytest.mark.complete("dpkg-deb --show b", cwd="dpkg") + def test_show(self, completion): + assert completion == "ash-completion-test-subject.deb" diff --git a/test/t/test_dpkg_query.py b/test/t/test_dpkg_query.py new file mode 100644 index 00000000..37c56211 --- /dev/null +++ b/test/t/test_dpkg_query.py @@ -0,0 +1,18 @@ +import os.path + +import pytest + + +@pytest.mark.bashcomp(cmd="dpkg-query",) +class TestDpkgQuery: + @pytest.mark.complete("dpkg-query --", require_cmd=True) + def test_options(self, completion): + assert completion + + @pytest.mark.xfail( + not os.path.exists("/etc/debian_version"), + reason="Likely fails on systems not based on Debian", + ) + @pytest.mark.complete("dpkg-query -W dpk", require_cmd=True) + def test_show(self, completion): + assert "dpkg" in completion diff --git a/test/t/test_feh.py b/test/t/test_feh.py index 51bd77b6..f2d5317b 100644 --- a/test/t/test_feh.py +++ b/test/t/test_feh.py @@ -16,11 +16,11 @@ class TestFeh: @pytest.mark.complete("feh -S pix") def test_3(self, completion): - assert completion == "pixels" + assert completion == "els" @pytest.mark.complete("feh --zoom ma") def test_4(self, completion): - assert completion == "max" + assert completion == "x" @pytest.mark.complete("feh -g 640") def test_5(self, completion): diff --git a/test/t/test_find.py b/test/t/test_find.py index a94e0e0d..9968ade7 100644 --- a/test/t/test_find.py +++ b/test/t/test_find.py @@ -14,10 +14,9 @@ class TestFind: def test_3(self, completion): assert completion - @pytest.mark.xfail # TODO: whitespace split issue @pytest.mark.complete("find -wholename ", cwd="shared/default") def test_4(self, completion): - assert completion == ["bar", "bar bar.d/", "foo", "foo foo.d/"] + assert completion == ["bar", "bar bar.d/", "foo", "foo.d/"] @pytest.mark.complete("find -uid ") def test_5(self, completion): @@ -26,3 +25,13 @@ class TestFind: @pytest.mark.complete("find -gid ") def test_6(self, completion): assert not [x for x in completion if not x.isdigit()] + + @pytest.mark.complete("find -exec shared/bin/ar") + def test_exec(self, completion): + assert completion == "p" + + # sh +: something that produces completions also when command is not + # available, and the chosen completion is not one of find's + @pytest.mark.complete("find /some/where -exec sh +") + def test_exec_args(self, completion): + assert "+o" in completion diff --git a/test/t/test_finger.py b/test/t/test_finger.py index 92c983fa..d765fdd7 100644 --- a/test/t/test_finger.py +++ b/test/t/test_finger.py @@ -1,16 +1,12 @@ import pytest -from conftest import assert_bash_exec +from conftest import assert_complete, partialize class TestFinger: @pytest.fixture(scope="class") - def users_at(self, bash): - return sorted( - assert_bash_exec( - bash, "compgen -A user -S @", want_output=True - ).split() - ) + def users_at(self, bash, output_sort_uniq): + return output_sort_uniq("compgen -u -S @") @pytest.mark.complete("finger ") def test_1(self, bash, completion, users_at): @@ -21,5 +17,17 @@ class TestFinger: if not any(x.startswith("r") for x in users_at): pytest.skip("No users starting with r") assert completion - assert all(x.startswith("r") for x in completion) + idx = 1 if len(completion) == 1 else 0 + assert completion == sorted( + x[idx:] for x in users_at if x.startswith("r") + ) assert not completion.endswith(" ") + + def test_partial_hostname(self, bash, known_hosts): + first_char, partial_hosts = partialize(bash, known_hosts) + user = "test" + completion = assert_complete(bash, "finger %s@%s" % (user, first_char)) + if len(completion) == 1: + assert completion == partial_hosts[0][1:] + else: + assert completion == ["%s@%s" % (user, x) for x in partial_hosts] diff --git a/test/t/test_gcc.py b/test/t/test_gcc.py index 87f25797..50906db5 100644 --- a/test/t/test_gcc.py +++ b/test/t/test_gcc.py @@ -24,7 +24,7 @@ class TestGcc: @pytest.mark.complete("gcc -fsanitize=add") def test_enum_value(self, completion, gcc_with_completion): - assert completion == "-fsanitize=address" + assert completion == "ress" @pytest.mark.complete("gcc -fsanitize=") def test_enum_value_with_eq(self, completion, gcc_with_completion): @@ -48,15 +48,12 @@ class TestGcc: @pytest.mark.complete("gcc --param=lto-max-p") def test_param_with_eq(self, completion, gcc_with_completion): - # starting with GCC 10.1 param end with = - assert ( - completion == "--param=lto-max-partition" - or completion == "--param=lto-max-partition=" - ) + # starting with GCC 10.1 param ends with = + assert completion in ("artition", "artition=") @pytest.mark.complete("gcc -march=amd") def test_march(self, completion, gcc_with_completion, gcc_x86): - assert completion == "-march=amdfam10" + assert completion == "fam10" @pytest.mark.complete("gcc -march=") def test_march_native(self, completion, gcc_with_completion): diff --git a/test/t/test_ip.py b/test/t/test_ip.py index 20752505..320647f4 100644 --- a/test/t/test_ip.py +++ b/test/t/test_ip.py @@ -9,3 +9,7 @@ class TestIp: @pytest.mark.complete("ip a ") def test_2(self, completion): assert completion + + @pytest.mark.complete("ip route replace ") + def test_r_r(self, completion): + assert completion diff --git a/test/t/test_ipcalc.py b/test/t/test_ipcalc.py new file mode 100644 index 00000000..5611674c --- /dev/null +++ b/test/t/test_ipcalc.py @@ -0,0 +1,23 @@ +import pytest + + +class TestIpcalc: + @pytest.mark.complete("ipcalc -", require_cmd=True) + def test_options(self, completion): + assert any(x in completion for x in "--help -h".split()) + + @pytest.mark.complete("ipcalc --split -") + def test_split_3args_1(self, completion): + assert not completion + + @pytest.mark.complete("ipcalc --split 1 -") + def test_split_3args_2(self, completion): + assert not completion + + @pytest.mark.complete("ipcalc --split 1 2 -") + def test_split_3args_3(self, completion): + assert not completion + + @pytest.mark.complete("ipcalc --split 1 2 3 -", require_cmd=True) + def test_split_3args_4(self, completion): + assert any(x in completion for x in "--help -h".split()) diff --git a/test/t/test_irb.py b/test/t/test_irb.py index 03a83c66..801d3739 100644 --- a/test/t/test_irb.py +++ b/test/t/test_irb.py @@ -6,6 +6,6 @@ class TestIrb: def test_1(self, completion): assert completion - @pytest.mark.complete("irb -", require_cmd=True) + @pytest.mark.complete("irb -", require_longopt=True) def test_options(self, completion): assert completion diff --git a/test/t/test_iscsiadm.py b/test/t/test_iscsiadm.py index 932ffeb5..885ca0ab 100644 --- a/test/t/test_iscsiadm.py +++ b/test/t/test_iscsiadm.py @@ -2,6 +2,6 @@ import pytest class TestIscsiadm: - @pytest.mark.complete("iscsiadm --mode") + @pytest.mark.complete("iscsiadm --mod") def test_1(self, completion): - assert completion + assert completion == "e" or "--mode" in completion diff --git a/test/t/test_isort.py b/test/t/test_isort.py index 9f7a6524..b142d1c4 100644 --- a/test/t/test_isort.py +++ b/test/t/test_isort.py @@ -6,6 +6,6 @@ class TestIsort: def test_1(self, completion): assert completion - @pytest.mark.complete("isort -", require_cmd=True) + @pytest.mark.complete("isort -", require_cmd=True, require_longopt=True) def test_2(self, completion): assert completion diff --git a/test/t/test_jsonschema.py b/test/t/test_jsonschema.py index 9e3929e6..6027f5d6 100644 --- a/test/t/test_jsonschema.py +++ b/test/t/test_jsonschema.py @@ -6,6 +6,8 @@ class TestJsonschema: def test_1(self, completion): assert completion - @pytest.mark.complete("jsonschema -", require_cmd=True) + @pytest.mark.complete( + "jsonschema -", require_cmd=True, require_longopt=True + ) def test_2(self, completion): assert completion diff --git a/test/t/test_kcov.py b/test/t/test_kcov.py index 3c7d3dfa..3e377ebe 100644 --- a/test/t/test_kcov.py +++ b/test/t/test_kcov.py @@ -8,7 +8,7 @@ class TestKcov: @pytest.mark.complete("kcov --exclude-patter", require_cmd=True) def test_2(self, completion): - assert completion == "--exclude-pattern=" + assert completion == "n=" assert completion.endswith("=") @pytest.mark.complete("kcov -l 42,") diff --git a/test/t/test_ldd.py b/test/t/test_ldd.py index 70e295a5..7f7201bd 100644 --- a/test/t/test_ldd.py +++ b/test/t/test_ldd.py @@ -6,6 +6,8 @@ class TestLdd: def test_1(self, completion): assert completion - @pytest.mark.complete("ldd -", require_cmd=True) + @pytest.mark.complete( + "ldd -", require_cmd=True, xfail="! ldd --help &>/dev/null" + ) def test_options(self, completion): assert completion diff --git a/test/t/test_less.py b/test/t/test_less.py index 0b14e21e..70833c34 100644 --- a/test/t/test_less.py +++ b/test/t/test_less.py @@ -5,3 +5,7 @@ class TestLess: @pytest.mark.complete("less --", require_longopt=True) def test_1(self, completion): assert completion + + @pytest.mark.complete("less --", require_longopt=True) + def test_no_dashdashdash(self, completion): + assert all(not x.startswith("---") for x in completion) diff --git a/test/t/test_lftp.py b/test/t/test_lftp.py index 765e51e1..f775a4c6 100644 --- a/test/t/test_lftp.py +++ b/test/t/test_lftp.py @@ -1,17 +1,15 @@ import pytest -from conftest import assert_bash_exec - @pytest.mark.bashcomp(pre_cmds=("HOME=$PWD/lftp",)) class TestLftp: - @pytest.mark.complete("lftp ") - def test_1(self, bash, completion): - hosts = assert_bash_exec( - bash, "compgen -A hostname", want_output=True - ).split() + @pytest.mark.complete("lftp ", require_cmd=True) + def test_1(self, bash, completion, output_sort_uniq): + hosts = output_sort_uniq("compgen -A hostname") assert all(x in completion for x in hosts) - assert "lftptest" in completion # defined in lftp/.lftp/bookmarks + # defined in lftp/.lftp/bookmarks + assert all(x in completion for x in "lftptest spacetest".split()) + assert "badbookmark" not in completion @pytest.mark.complete("lftp -", require_cmd=True) def test_2(self, completion): diff --git a/test/t/test_lilo.py b/test/t/test_lilo.py index 9783f506..2c698212 100644 --- a/test/t/test_lilo.py +++ b/test/t/test_lilo.py @@ -5,3 +5,12 @@ class TestLilo: @pytest.mark.complete("lilo -") def test_1(self, completion): assert completion + + @pytest.mark.complete("lilo -C lilo/lilo.conf -D ") + def test_labels(self, completion): + # Note that 2.4.33 should not be here, it's commented out + assert completion == sorted("try tamu PCDOS WinXP oldDOS".split()) + + @pytest.mark.complete("lilo -C -D ") + def test_labels_incorrect_command(self, completion): + assert not completion diff --git a/test/t/test_ls.py b/test/t/test_ls.py index 7e2d1f35..8abcb59d 100644 --- a/test/t/test_ls.py +++ b/test/t/test_ls.py @@ -36,5 +36,5 @@ class TestLs: return part, full = part_full completion = assert_complete(bash, "ls ~%s" % part) - assert completion == "~%s" % full + assert completion == full[len(part) :] assert completion.endswith(" ") diff --git a/test/t/test_lspci.py b/test/t/test_lspci.py index ac18da3f..aba7b5a4 100644 --- a/test/t/test_lspci.py +++ b/test/t/test_lspci.py @@ -6,6 +6,8 @@ class TestLspci: def test_1(self, completion): assert completion - @pytest.mark.complete("lspci -A ", require_cmd=True) + @pytest.mark.complete( + "lspci -A ", require_cmd=True, skipif="! lspci -A help &>/dev/null" + ) def test_2(self, completion): assert completion diff --git a/test/t/test_make.py b/test/t/test_make.py index e6e043cd..19861b00 100644 --- a/test/t/test_make.py +++ b/test/t/test_make.py @@ -2,21 +2,19 @@ import os import pytest -from conftest import in_container - class TestMake: @pytest.mark.complete("make -f Ma", cwd="make") def test_1(self, completion): - assert completion == "Makefile" + assert completion == "kefile" - @pytest.mark.complete("make .", cwd="make") + @pytest.mark.complete("make .", cwd="make", require_cmd=True) def test_2(self, bash, completion): """Hidden targets.""" assert completion == ".cache/ .test_passes".split() os.remove("%s/make/%s" % (bash.cwd, "extra_makefile")) - @pytest.mark.complete("make .cache/", cwd="make") + @pytest.mark.complete("make .cache/", cwd="make", require_cmd=True) def test_3(self, bash, completion): assert completion == "1 2".split() os.remove("%s/make/%s" % (bash.cwd, "extra_makefile")) @@ -29,22 +27,17 @@ class TestMake: def test_5(self, completion): assert completion - @pytest.mark.complete("make ", cwd="make") + @pytest.mark.complete("make ", cwd="make", require_cmd=True) def test_6(self, bash, completion): assert completion == "all clean extra_makefile install sample".split() os.remove("%s/make/%s" % (bash.cwd, "extra_makefile")) - @pytest.mark.xfail( - in_container() and os.environ.get("DIST") == "centos6", - reason="Fails for some unknown reason on CentOS 6, " - "even though the behavior appears to be correct", - ) - @pytest.mark.complete("make .cache/.", cwd="make") + @pytest.mark.complete("make .cache/.", cwd="make", require_cmd=True) def test_7(self, bash, completion): assert completion == ".1 .2".split() os.remove("%s/make/%s" % (bash.cwd, "extra_makefile")) - @pytest.mark.complete("make -C make ") + @pytest.mark.complete("make -C make ", require_cmd=True) def test_8(self, bash, completion): assert completion == "all clean extra_makefile install sample".split() os.remove("%s/make/%s" % (bash.cwd, "extra_makefile")) diff --git a/test/t/test_man.py b/test/t/test_man.py index ad36d96e..1ff9f84b 100644 --- a/test/t/test_man.py +++ b/test/t/test_man.py @@ -1,8 +1,6 @@ -import os - import pytest -from conftest import assert_bash_exec, in_container +from conftest import assert_bash_exec @pytest.mark.bashcomp( @@ -36,24 +34,16 @@ class TestMan: require_cmd=True, ) def test_1(self, completion): - assert completion == "bash-completion-testcase" + assert completion == "e" @pytest.mark.complete("man man1/f", cwd="man", env=dict(MANPATH=manpath)) def test_2(self, completion): - assert completion == "man1/foo.1" + assert completion == "oo.1" @pytest.mark.complete("man man/", cwd="man", env=dict(MANPATH=manpath)) def test_3(self, completion): - assert completion == "man/quux.8" - - @pytest.mark.xfail( - in_container() and os.environ.get("DIST") == "centos6", - reason="TODO: Fails in CentOS for some reason, unknown " - "how to trigger same behavior as tests show (is " - "different and correct when tried manually, but here " - "at least in CI completes things it should not with " - "this MANPATH setting)", - ) + assert completion == "quux.8" + @pytest.mark.complete( "man %s" % assumed_present, cwd="shared/empty_dir", @@ -82,7 +72,7 @@ class TestMan: env=dict(MANPATH="%s:" % manpath), ) def test_6(self, completion): - assert completion == "bash-completion-testcase" + assert completion == "e" @pytest.mark.complete( "man %s" % assumed_present, @@ -100,7 +90,7 @@ class TestMan: env=dict(MANPATH=":%s" % manpath), ) def test_8(self, completion): - assert completion == "bash-completion-testcase" + assert completion == "e" @pytest.mark.complete( "man %s" % assumed_present, @@ -118,7 +108,7 @@ class TestMan: env=dict(MANPATH="%s:../tmp/man" % manpath), ) def test_10(self, bash, colonpath, completion): - assert completion == "Bash::Completion" + assert completion == "ompletion" @pytest.mark.complete("man -", require_cmd=True) def test_11(self, completion): diff --git a/test/t/test_mkdir.py b/test/t/test_mkdir.py index 1b9cb9dc..afc3fd04 100644 --- a/test/t/test_mkdir.py +++ b/test/t/test_mkdir.py @@ -6,10 +6,9 @@ class TestMkdir: def test_1(self, completion): assert completion - @pytest.mark.xfail # TODO: whitespace split issue @pytest.mark.complete("mkdir ", cwd="shared/default") def test_2(self, completion): - assert completion == ["bar bar.d/", "foo", "foo.d/"] + assert completion == ["bar", "bar bar.d/", "foo", "foo.d/"] @pytest.mark.xfail # TODO: why path in completion, basename in .output? @pytest.mark.complete("mkdir shared/default/foo.d/") diff --git a/test/t/test_modprobe.py b/test/t/test_modprobe.py index 38d290ae..9201119d 100644 --- a/test/t/test_modprobe.py +++ b/test/t/test_modprobe.py @@ -6,7 +6,7 @@ import pytest class TestModprobe: @pytest.mark.complete("modprobe --al") def test_1(self, completion): - assert completion == "--all" + assert completion == "l" # "in": intel*, ... @pytest.mark.complete( diff --git a/test/t/test_mount.py b/test/t/test_mount.py index fbd6dcae..8254fd40 100644 --- a/test/t/test_mount.py +++ b/test/t/test_mount.py @@ -12,7 +12,7 @@ class TestMount: @pytest.mark.complete("mount /dev/sda1 def", cwd="shared") def test_3(self, completion): - assert completion == "default/" + assert completion == "ault/" assert not completion.endswith(" ") @pytest.mark.complete( diff --git a/test/t/test_mr.py b/test/t/test_mr.py index 768e1b35..bfad643f 100644 --- a/test/t/test_mr.py +++ b/test/t/test_mr.py @@ -19,7 +19,7 @@ class TestMr: "mr -c shared/default/foo.d/", xfail="! man -h &>/dev/null" ) def test_3(self, completion): - assert completion == "shared/default/foo.d/foo" + assert completion == "foo" @pytest.mark.complete( "mr bootstrap shared/default/", @@ -29,18 +29,21 @@ class TestMr: def test_4(self, completion): assert completion == ["bar", "bar bar.d/", "foo", "foo.d/"] - @pytest.mark.xfail # "clean" doesn't exist before mr 1.20141023 @pytest.mark.complete( - "mr clean -", require_cmd=True, xfail="! man -h &>/dev/null" + "mr clean -", + require_cmd=True, + xfail="! man -h &>/dev/null", + # "clean" does not exist before mr 1.20141023 + skipif="! mr help 2>&1 | command grep -qwF clean", ) def test_5(self, completion): - assert completion == "-f" + assert completion == "f" @pytest.mark.complete( "mr commit -", require_cmd=True, xfail="! man -h &>/dev/null" ) def test_6(self, completion): - assert completion == "-m" + assert completion == "m" @pytest.mark.complete( "mr status ", require_cmd=True, xfail="! man -h &>/dev/null" diff --git a/test/t/test_mypy.py b/test/t/test_mypy.py index 63fc916c..11628c1d 100644 --- a/test/t/test_mypy.py +++ b/test/t/test_mypy.py @@ -6,7 +6,7 @@ class TestMypy: def test_1(self, completion): assert completion - @pytest.mark.complete("mypy --", require_cmd=True) + @pytest.mark.complete("mypy --", require_cmd=True, require_longopt=True) def test_2(self, completion): assert completion diff --git a/test/t/test_nmap.py b/test/t/test_nmap.py index a4d8a899..9aff8b29 100644 --- a/test/t/test_nmap.py +++ b/test/t/test_nmap.py @@ -1,7 +1,47 @@ import pytest +from conftest import assert_bash_exec + class TestNmap: - @pytest.mark.complete("nmap --v") - def test_1(self, completion): + @pytest.fixture(scope="class") + def functions(self, request, bash): + assert_bash_exec(bash, "_mock_nmap() { cat nmap/nmap-h.txt; }") + assert_bash_exec(bash, "complete -F _nmap _mock_nmap") + + @pytest.mark.complete("nmap --v", require_cmd=True) + def test_live_options(self, completion): + assert completion + + @pytest.mark.complete("nmap ") + def test_hosts(self, completion): assert completion + + @pytest.mark.complete("_mock_nmap -") + def test_mock_options(self, completion, functions): + assert completion == sorted( + "-iL -iR --exclude --excludefile -sL -sn -Pn -PS -PA -PU -PY -PE " + "-PP -PM -PO -n -R --dns-servers --system-dns --traceroute -sS " + "-sT -sA -sW -sM -sU -sN -sF -sX --scanflags -sI -sY -sZ -sO -b " + "-p --exclude-ports -F -r --top-ports --port-ratio -sV " + "--version-intensity --version-light --version-all " + "--version-trace -sC --script= --script-args= --script-args-file= " + "--script-trace --script-updatedb --script-help= -O " + "--osscan-limit --osscan-guess " + # TODO: -T known mishandled; should expand -T<0-5> to -T0 ... -T5 + "-T --min-hostgroup --max-hostgroup --min-parallelism " + "--max-parallelism --min-rtt-timeout --max-rtt-timeout " + "--initial-rtt-timeout --max-retries --host-timeout --scan-delay " + "--max-scan-delay --min-rate --max-rate -f --mtu -D -S -e -g " + "--source-port --proxies --data --data-string --data-length " + "--ip-options --ttl --spoof-mac --badsum -oN -oX -oS -oG -oA -v " + "-d --reason --open --packet-trace --iflist --append-output " + "--resume --stylesheet --webxml --no-stylesheet -6 -A --datadir " + "--send-eth --send-ip --privileged --unprivileged -V -h" + "".strip().split() + ) + + @pytest.mark.complete("_mock_nmap --script-args-f") + def test_mock_nospace(self, completion, functions): + assert completion == "ile=" + assert completion.endswith("=") # no space appended diff --git a/test/t/test_openssl.py b/test/t/test_openssl.py index e3af3530..3eaf6d47 100644 --- a/test/t/test_openssl.py +++ b/test/t/test_openssl.py @@ -6,10 +6,11 @@ class TestOpenssl: def test_1(self, completion): assert completion - @pytest.mark.complete("openssl pkey -cipher ") + @pytest.mark.complete("openssl pkey -cipher ", require_cmd=True) def test_2(self, completion): assert completion - @pytest.mark.complete("openssl dgst -s") + @pytest.mark.complete("openssl dgst -s", require_cmd=True) def test_3(self, completion): assert completion + assert any(x.startswith("-sha") for x in completion) diff --git a/test/t/test_perl.py b/test/t/test_perl.py index c8baa2f3..049c91ea 100644 --- a/test/t/test_perl.py +++ b/test/t/test_perl.py @@ -63,7 +63,7 @@ class TestPerl: @pytest.mark.complete("perl -xshared/default/b") def test_14(self, completion): """-x without space should complete dirs.""" - assert completion == ["-xshared/default/bar bar.d/"] + assert completion == r"ar\ bar.d/" @pytest.mark.complete("perl -x shared/default/b") def test_15(self, completion): diff --git a/test/t/test_pgrep.py b/test/t/test_pgrep.py index 9c233311..9a998edb 100644 --- a/test/t/test_pgrep.py +++ b/test/t/test_pgrep.py @@ -11,3 +11,25 @@ class TestPgrep: @pytest.mark.complete("pgrep -", require_cmd=True) def test_2(self, completion): assert completion + + @pytest.mark.complete( + "pgrep --nslist ", + require_cmd=True, + skipif=( + "! pgrep --help 2>&1 | command grep -qF 'Available namespaces'" + ), + ) + def test_nslist(self, completion): + assert completion + assert not any("," in x for x in completion) + + @pytest.mark.complete( + "pgrep --nslist foo,", + require_cmd=True, + skipif=( + "! pgrep --help 2>&1 | command grep -qF 'Available namespaces'" + ), + ) + def test_nslist_after_comma(self, completion): + assert completion + assert not any("," in x for x in completion) diff --git a/test/t/test_postfix.py b/test/t/test_postfix.py index 67a898d1..10020b0b 100644 --- a/test/t/test_postfix.py +++ b/test/t/test_postfix.py @@ -1,3 +1,5 @@ +import getpass + import pytest @@ -6,7 +8,15 @@ class TestPostfix: def test_1(self, completion): assert completion - @pytest.mark.xfail # see TODO in completion - @pytest.mark.complete("postfix -", require_cmd=True) - def test_2(self, completion): + @pytest.mark.xfail( + getpass.getuser() != "root", + reason="Likely outputs usage only for root", + ) + @pytest.mark.complete( + "postfix -", + require_cmd=True, + xfail="! type unbuffer &>/dev/null", + sleep_after_tab=2, # postfix is slow to output usage + ) + def test_options(self, completion): assert completion diff --git a/test/t/test_printenv.py b/test/t/test_printenv.py new file mode 100644 index 00000000..540c4f64 --- /dev/null +++ b/test/t/test_printenv.py @@ -0,0 +1,19 @@ +import pytest + + +class TestPrintenv: + @pytest.mark.complete("printenv ") + def test_empty(self, completion): + assert completion + + @pytest.mark.complete("printenv PAT") + def test_path(self, completion): + assert completion == "H" or "PATH" in completion + + @pytest.mark.complete( + "printenv -", + require_cmd=True, + xfail="! printenv --help 2>&1 | command grep -qF -- ' -'", + ) + def test_options(self, completion): + assert completion diff --git a/test/t/test_protoc.py b/test/t/test_protoc.py index e890c56a..744b99f4 100644 --- a/test/t/test_protoc.py +++ b/test/t/test_protoc.py @@ -9,3 +9,12 @@ class TestProtoc: @pytest.mark.complete("protoc -", require_cmd=True) def test_2(self, completion): assert completion + assert any( + x.endswith("_out") or x.endswith("_out=") for x in completion + ) + + @pytest.mark.complete( + "protoc --non_existent_plugin_out ", cwd="shared/default" + ) + def test_all_out(self, completion): + assert completion == ["bar bar.d/", "foo.d/"] diff --git a/test/t/test_pydocstyle.py b/test/t/test_pydocstyle.py index caa87902..1f443208 100644 --- a/test/t/test_pydocstyle.py +++ b/test/t/test_pydocstyle.py @@ -6,6 +6,8 @@ class TestPydocstyle: def test_1(self, completion): assert completion - @pytest.mark.complete("pydocstyle -", require_cmd=True) + @pytest.mark.complete( + "pydocstyle -", require_cmd=True, require_longopt=True + ) def test_2(self, completion): assert completion diff --git a/test/t/test_pylint.py b/test/t/test_pylint.py index 4b799532..43a4c43f 100644 --- a/test/t/test_pylint.py +++ b/test/t/test_pylint.py @@ -2,7 +2,7 @@ import pytest class TestPylint: - @pytest.mark.complete("pylint --v", require_cmd=True) + @pytest.mark.complete("pylint --v", require_cmd=True, require_longopt=True) def test_1(self, completion): assert completion diff --git a/test/t/test_pytest.py b/test/t/test_pytest.py index 69d01820..e70c7a5d 100644 --- a/test/t/test_pytest.py +++ b/test/t/test_pytest.py @@ -1,3 +1,5 @@ +import inspect + import pytest @@ -9,3 +11,40 @@ class TestPytest: @pytest.mark.complete("pytest -") def test_2(self, completion): assert completion + + @pytest.mark.complete("pytest ../t/test_pytest.py:") + def test_classes_and_functions(self, completion): + assert completion == ":TestPytest :test_function_canary".split() + + @pytest.mark.complete("pytest ../t/test_pytest.py::TestPytest::") + def test_class_methods(self, completion): + methods = [ + x[0] + for x in inspect.getmembers(self, predicate=inspect.ismethod) + if x[0].startswith("test_") + ] + assert completion == methods + + @pytest.mark.complete("pytest pytest/test_async.py:") + def test_classes_and_async_functions(self, completion): + assert completion == ":Testing :test_positive".split() + + @pytest.mark.complete("pytest pytest/test_async.py::Testing::") + def test_async_class_methods(self, completion): + assert completion == "test_positive" + + def non_test_cananary_method(self): + pass + + +def test_function_canary(): + pass + + +def non_test_canary(): + pass + + +class NonTestCanaryClass: + def test_is_this_function_not(self): + pass diff --git a/test/t/test_python.py b/test/t/test_python.py index 57802721..5308dcb1 100644 --- a/test/t/test_python.py +++ b/test/t/test_python.py @@ -33,3 +33,7 @@ class TestPython: @pytest.mark.complete("python -m sy", require_cmd=True) def test_8(self, completion): assert completion + + @pytest.mark.complete("python -m json.", require_cmd=True) + def test_9(self, completion): + assert "json.tool" in completion diff --git a/test/t/test_python3.py b/test/t/test_python3.py index b968a34e..a4f6d966 100644 --- a/test/t/test_python3.py +++ b/test/t/test_python3.py @@ -33,3 +33,7 @@ class TestPython3: @pytest.mark.complete("python3 -m sy", require_cmd=True) def test_8(self, completion): assert completion + + @pytest.mark.complete("python3 -m json.", require_cmd=True) + def test_9(self, completion): + assert "json.tool" in completion diff --git a/test/t/test_ri.py b/test/t/test_ri.py index 9430b667..420b6cbb 100644 --- a/test/t/test_ri.py +++ b/test/t/test_ri.py @@ -13,4 +13,4 @@ class TestRi: @pytest.mark.complete("ri BashCompletio", require_cmd=True) def test_3(self, completion): - assert completion == "BashCompletion" + assert completion == "n" diff --git a/test/t/test_sbcl.py b/test/t/test_sbcl.py index cce4cba3..f05741a7 100644 --- a/test/t/test_sbcl.py +++ b/test/t/test_sbcl.py @@ -2,7 +2,6 @@ import pytest class TestSbcl: - @pytest.mark.xfail # TODO: whitespace split issue @pytest.mark.complete("sbcl shared/default/") def test_1(self, completion): - assert completion == ["bar", "bar bar.d/", "foo", "foo foo.d/"] + assert completion == ["bar", "bar bar.d/", "foo", "foo.d/"] diff --git a/test/t/test_sbcl_mt.py b/test/t/test_sbcl_mt.py index d8049f3f..c3965393 100644 --- a/test/t/test_sbcl_mt.py +++ b/test/t/test_sbcl_mt.py @@ -3,7 +3,6 @@ import pytest @pytest.mark.bashcomp(cmd="sbcl-mt") class TestSbclMt: - @pytest.mark.xfail # TODO: whitespace split issue @pytest.mark.complete("sbcl-mt shared/default/") def test_1(self, completion): - assert completion == ["bar", "bar bar.d/", "foo", "foo foo.d/"] + assert completion == ["bar", "bar bar.d/", "foo", "foo.d/"] diff --git a/test/t/test_scp.py b/test/t/test_scp.py new file mode 100644 index 00000000..66b8da22 --- /dev/null +++ b/test/t/test_scp.py @@ -0,0 +1,79 @@ +from itertools import chain + +import pytest + +from conftest import assert_bash_exec + +LIVE_HOST = "bash_completion" + + +class TestScp: + @pytest.mark.complete("scp -F config ", cwd="scp") + def test_basic(self, hosts, completion): + expected = sorted( + chain( + ( + "%s:" % x + for x in chain( + hosts, + # From fixtures/scp/config + "gee hut".split(), + # From fixtures/scp/known_hosts + "blah doo ike".split(), + ) + ), + # Local filenames + ["config", "known_hosts", r"spaced\ \ conf"], + ) + ) + assert completion == expected + + @pytest.mark.complete("scp -F 'spaced conf' ", cwd="scp") + def test_basic_spaced_conf(self, hosts, completion): + expected = sorted( + chain( + ( + "%s:" % x + for x in chain( + hosts, + # From "fixtures/scp/spaced conf" + "gee jar".split(), + # From fixtures/scp/known_hosts + "blah doo ike".split(), + ) + ), + # Local filenames + ["config", "known_hosts", r"spaced\ \ conf"], + ) + ) + assert completion == expected + + @pytest.mark.complete("scp -F") + def test_capital_f_without_space(self, completion): + assert completion + assert not any( + "option requires an argument -- F" in x for x in completion + ) + + @pytest.fixture(scope="class") + def live_pwd(self, bash): + try: + return assert_bash_exec( + bash, + "ssh -o 'Batchmode yes' -o 'ConnectTimeout 1' " + "%s pwd 2>/dev/null" % LIVE_HOST, + want_output=True, + ).strip() + except AssertionError: + pytest.skip("Live host %s not available" % LIVE_HOST) + + @pytest.mark.complete("scp %s:" % LIVE_HOST, sleep_after_tab=2) + def test_live(self, live_pwd, completion): + """ + To support this test, configure a HostName entry for LIVE_HOST + in ssh's configs, e.g. ~/.ssh/config or /etc/ssh/ssh_config. + + Connection to it must open sufficiently quickly for the + ConnectTimeout and sleep_after_tab settings. + """ + assert completion == "%s:%s/" % (LIVE_HOST, live_pwd) diff --git a/test/t/test_screen.py b/test/t/test_screen.py index d9254bda..3e98837f 100644 --- a/test/t/test_screen.py +++ b/test/t/test_screen.py @@ -19,17 +19,17 @@ class TestScreen: def test_4(self, completion): assert completion - @pytest.mark.complete("screen -T foo cat") + @pytest.mark.complete("screen -T foo ca") def test_5(self, completion): - assert completion + assert completion == "t" or "cat" in completion @pytest.mark.complete("screen //") def test_telnet(self, completion): - assert completion == "//telnet" + assert completion == "telnet" @pytest.mark.complete("screen cat //") def test_not_telnet(self, completion): - assert completion != "//telnet" + assert completion != "telnet" @pytest.mark.complete("screen //telnet ", env=dict(HOME="$PWD/shared")) def test_telnet_first_arg(self, completion): diff --git a/test/t/test_secret_tool.py b/test/t/test_secret_tool.py new file mode 100644 index 00000000..cbfc0cbc --- /dev/null +++ b/test/t/test_secret_tool.py @@ -0,0 +1,12 @@ +import pytest + + +@pytest.mark.bashcomp(cmd="secret-tool",) +class TestSecretTool: + @pytest.mark.complete("secret-tool ", require_cmd=True) + def test_modes(self, completion): + assert "store" in completion + + @pytest.mark.complete("secret-tool search ") + def test_no_complete(self, completion): + assert not completion diff --git a/test/t/test_sftp.py b/test/t/test_sftp.py index 0c039399..a421a449 100644 --- a/test/t/test_sftp.py +++ b/test/t/test_sftp.py @@ -1,11 +1,46 @@ +from itertools import chain + import pytest class TestSftp: @pytest.mark.complete("sftp -Fsp", cwd="sftp") def test_1(self, completion): - assert completion == "-Fspaced conf" + assert completion == r"aced\ \ conf" @pytest.mark.complete("sftp -", require_cmd=True) def test_2(self, completion): assert completion + + @pytest.mark.complete("sftp -F config ", cwd="sftp") + def test_hosts(self, hosts, completion): + expected = sorted( + chain( + hosts, + # From fixtures/sftp/config + "gee hut".split(), + # From fixtures/sftp/known_hosts + "10.10.10.10 doo ike".split(), + ) + ) + assert completion == expected + + @pytest.mark.complete(r"sftp -F spaced\ \ conf ", cwd="sftp") + def test_hosts_spaced_conf(self, hosts, completion): + expected = sorted( + chain( + hosts, + # From "fixtures/sftp/spaced conf" + "gee jar".split(), + # From fixtures/sftp/known_hosts + "10.10.10.10 doo ike".split(), + ) + ) + assert completion == expected + + @pytest.mark.complete("sftp -F") + def test_capital_f_without_space(self, completion): + assert completion + assert not any( + "option requires an argument -- F" in x for x in completion + ) diff --git a/test/t/test_slapt_get.py b/test/t/test_slapt_get.py index 626dde9e..92449711 100644 --- a/test/t/test_slapt_get.py +++ b/test/t/test_slapt_get.py @@ -1,8 +1,26 @@ +import os.path +from tempfile import mkstemp + import pytest +from conftest import assert_complete, is_bash_type + @pytest.mark.bashcomp(cmd="slapt-get") class TestSlaptGet: + @pytest.fixture(scope="class") + def slapt_getrc(self, request, bash): + fd, fname = mkstemp(prefix="slapt-getrc.", text=True) + request.addfinalizer(lambda: os.remove(fname)) + with os.fdopen(fd, "w") as f: + print( + "WORKINGDIR=%s/" + % os.path.join(bash.cwd, *"slackware var slapt-get".split()), + file=f, + ) + print("SOURCE=file:///home/", file=f) + return fname + @pytest.mark.complete("slapt-get -", require_cmd=True) def test_1(self, completion): assert completion @@ -14,3 +32,13 @@ class TestSlaptGet: @pytest.mark.complete("slapt-get -c non-existent-file --install ") def test_3(self, completion): assert not completion + + def test_install(self, bash, slapt_getrc): + if not is_bash_type(bash, "slapt-get"): + pytest.skip("slapt-get not found") + completion = assert_complete( + bash, "slapt-get -c %s --install " % slapt_getrc + ) + assert completion == sorted( + "abc-4-i686-1 ran-1.2-noarch-1 qwe-2.1-i486-1".split() + ) diff --git a/test/t/test_slapt_src.py b/test/t/test_slapt_src.py index dd443b04..b55b722d 100644 --- a/test/t/test_slapt_src.py +++ b/test/t/test_slapt_src.py @@ -1,16 +1,43 @@ +import os +from tempfile import mkstemp + import pytest +from conftest import assert_complete, is_bash_type + @pytest.mark.bashcomp(cmd="slapt-src") class TestSlaptSrc: + @pytest.fixture(scope="class") + def slapt_srcrc(self, request, bash): + fd, fname = mkstemp(prefix="slapt-srcrc.", text=True) + request.addfinalizer(lambda: os.remove(fname)) + with os.fdopen(fd, "w") as f: + print( + "BUILDDIR=%s/" + % os.path.join( + bash.cwd, *"slackware usr src slapt-src".split() + ), + file=f, + ) + return fname + @pytest.mark.complete("slapt-src -", require_cmd=True) def test_1(self, completion): assert completion @pytest.mark.complete("slapt-src --bu", require_cmd=True) def test_2(self, completion): - assert completion == "--build" + assert completion == "ild" or "--build" in completion @pytest.mark.complete("slapt-src --ins", require_cmd=True) def test_3(self, completion): - assert completion == "--install" + assert completion == "tall" or "--install" in completion + + def test_install(self, bash, slapt_srcrc): + if not is_bash_type(bash, "slapt-src"): + pytest.skip("slapt-src not found") + completion = assert_complete( + bash, "slapt-src --config %s --install " % slapt_srcrc + ) + assert completion == "abc:4 qwe:2.1".split() diff --git a/test/t/test_ssh.py b/test/t/test_ssh.py index 204b7c7c..8e958195 100644 --- a/test/t/test_ssh.py +++ b/test/t/test_ssh.py @@ -1,10 +1,12 @@ import pytest +from conftest import assert_complete, partialize + class TestSsh: @pytest.mark.complete("ssh -Fsp", cwd="ssh") def test_1(self, completion): - assert completion == "-Fspaced conf" + assert completion == r"aced\ \ conf" @pytest.mark.complete("ssh -F config ls", cwd="ssh") def test_2(self, completion): @@ -32,3 +34,27 @@ class TestSsh: @pytest.mark.complete("ssh -", require_cmd=True) def test_6(self, completion): assert completion + + @pytest.mark.complete("ssh -F") + def test_capital_f_without_space(self, completion): + assert completion + assert not any( + "option requires an argument -- F" in x for x in completion + ) + + @pytest.mark.complete("ssh -F nonexistent ") + def test_capital_f_nonexistent(self, completion): + assert completion + + def test_partial_hostname(self, bash, known_hosts): + first_char, partial_hosts = partialize(bash, known_hosts) + completion = assert_complete(bash, "ssh %s" % first_char) + if len(completion) == 1: + assert completion == partial_hosts[0][1:] + else: + assert completion == sorted(x for x in partial_hosts) + + @pytest.mark.parametrize("protocol", "4 6 9".split()) + def test_protocol_option_bundling(self, bash, protocol): + completion = assert_complete(bash, "ssh -%sF ssh/" % protocol) + assert "config" in completion diff --git a/test/t/test_ssh_keygen.py b/test/t/test_ssh_keygen.py index 2d53f5f8..b773ab4f 100644 --- a/test/t/test_ssh_keygen.py +++ b/test/t/test_ssh_keygen.py @@ -6,3 +6,54 @@ class TestSshKeygen: @pytest.mark.complete("ssh-keygen -", require_cmd=True) def test_1(self, completion): assert completion + + @pytest.mark.complete("ssh-keygen -s foo_key ssh-copy-id/.ssh/") + def test_filedir_pub_at_end_of_s(self, completion): + assert completion + assert all(x.endswith(".pub") for x in completion) + + @pytest.mark.complete("ssh-keygen -s foo_key -n foo,") + def test_usernames_for_n(self, completion): + assert completion + assert not any("," in x for x in completion) + # TODO check that these are usernames + + @pytest.mark.complete("ssh-keygen -s foo_key -h -n foo,") + def test_host_for_h_n(self, completion): + assert completion + assert not any("," in x for x in completion) + # TODO check that these are hostnames + + @pytest.mark.complete("ssh-keygen -Y foo -n ") + def test_n_with_Y(self, completion): + assert not completion + + @pytest.mark.complete("ssh-keygen -r ") + def test_r_without_Y(self, completion): + assert not completion + + @pytest.mark.complete("ssh-keygen -Y foo -r ") + def test_r_with_Y(self, completion): + assert "ssh/" in completion + + @pytest.mark.complete("ssh-keygen -t ecdsa -b ") + def test_ecdsa_b(self, completion): + assert completion + + @pytest.mark.complete("ssh-keygen -t ecdsa-sk -b ") + def test_ecdsa_sk_b(self, completion): + assert not completion + + @pytest.mark.complete("ssh-keygen -O ") + def test_O(self, completion): + assert completion + assert any(x.endswith("=") for x in completion) + + @pytest.mark.complete("ssh-keygen -O force-command=bas") + def test_O_force_command(self, completion): + assert completion + assert not completion.startswith("force-command=") + + @pytest.mark.complete("ssh-keygen -O unknown=") + def test_O_unknown(self, completion): + assert not completion diff --git a/test/t/test_sudo.py b/test/t/test_sudo.py index ced6662e..a3494664 100644 --- a/test/t/test_sudo.py +++ b/test/t/test_sudo.py @@ -10,27 +10,27 @@ class TestSudo: @pytest.mark.complete("sudo cd fo", cwd="shared/default") def test_2(self, completion): - assert completion == "foo.d/" + assert completion == "o.d/" assert not completion.endswith(" ") @pytest.mark.complete("sudo sh share") def test_3(self, completion): - assert completion == "shared/" + assert completion == "d/" assert not completion.endswith(" ") @pytest.mark.complete("sudo mount /dev/sda1 def", cwd="shared") def test_4(self, completion): - assert completion == "default/" + assert completion == "ault/" assert not completion.endswith(" ") @pytest.mark.complete("sudo -e -u root bar foo", cwd="shared/default") def test_5(self, completion): - assert completion == ["foo", "foo.d/"] + assert completion == "foo foo.d/".split() def test_6(self, bash, part_full_user): part, full = part_full_user completion = assert_complete(bash, "sudo chown %s" % part) - assert completion == full + assert completion == full[len(part) :] assert completion.endswith(" ") def test_7(self, bash, part_full_user, part_full_group): @@ -39,32 +39,32 @@ class TestSudo: completion = assert_complete( bash, "sudo chown %s:%s" % (user, partgroup) ) - assert completion == "%s:%s" % (user, fullgroup) + assert completion == fullgroup[len(partgroup) :] assert completion.endswith(" ") def test_8(self, bash, part_full_group): part, full = part_full_group completion = assert_complete(bash, "sudo chown dot.user:%s" % part) - assert completion == "dot.user:%s" % full + assert completion == full[len(part) :] assert completion.endswith(" ") - @pytest.mark.xfail # TODO check escaping, whitespace - def test_9(self, bash, part_full_group): - """Test preserving special chars in $prefix$partgroup<TAB>.""" - part, full = part_full_group - for prefix in ( + @pytest.mark.parametrize( + "prefix", + [ r"funky\ user:", "funky.user:", r"funky\.user:", r"fu\ nky.user:", r"f\ o\ o\.\bar:", r"foo\_b\ a\.r\ :", - ): - completion = assert_complete( - bash, "sudo chown %s%s" % (prefix, part) - ) - assert completion == "%s%s" % (prefix, full) - assert completion.endswith(" ") + ], + ) + def test_9(self, bash, part_full_group, prefix): + """Test preserving special chars in $prefix$partgroup<TAB>.""" + part, full = part_full_group + completion = assert_complete(bash, "sudo chown %s%s" % (prefix, part)) + assert completion == full[len(part) :] + assert completion.endswith(" ") def test_10(self, bash, part_full_user, part_full_group): """Test giving up on degenerate cases instead of spewing junk.""" diff --git a/test/t/test_tar.py b/test/t/test_tar.py index 309bcc76..4518d0bd 100644 --- a/test/t/test_tar.py +++ b/test/t/test_tar.py @@ -77,28 +77,22 @@ class TestTar: @pytest.mark.complete("tar --add-fil") def test_15(self, completion, gnu_tar): - assert completion == "--add-file=" + assert completion == "e=" assert not completion.endswith(" ") @pytest.mark.complete("tar -cf /dev/null --posi") def test_16(self, completion, gnu_tar): - assert completion == "--posix" + assert completion == "x" assert completion.endswith(" ") @pytest.mark.complete("tar --owner=") - def test_17(self, bash, completion, gnu_tar): - users = sorted( - assert_bash_exec(bash, "compgen -A user", want_output=True).split() - ) + def test_17(self, bash, completion, gnu_tar, output_sort_uniq): + users = output_sort_uniq("compgen -u") assert completion == users @pytest.mark.complete("tar --group=") - def test_18(self, bash, completion, gnu_tar): - groups = sorted( - assert_bash_exec( - bash, "compgen -A group", want_output=True - ).split() - ) + def test_18(self, bash, completion, gnu_tar, output_sort_uniq): + groups = output_sort_uniq("compgen -g") assert completion == groups # Use -b for this as -b is still not handled by tar's completion @@ -121,6 +115,6 @@ class TestTar: @pytest.mark.complete(r"tar tf escape.tar a/b\'", cwd="tar") def test_22(self, bash, completion): """Test listing escaped chars in old option.""" - assert completion == "a/b'c/" + assert completion == "c/" # TODO: "tar tf escape.tar a/b" diff --git a/test/t/test_totem.py b/test/t/test_totem.py new file mode 100644 index 00000000..f6fb26fe --- /dev/null +++ b/test/t/test_totem.py @@ -0,0 +1,7 @@ +import pytest + + +class TestTotem: + @pytest.mark.complete("totem ") + def test_basic(self, completion): + assert completion diff --git a/test/t/test_tshark.py b/test/t/test_tshark.py index 8ed881ee..f49533e0 100644 --- a/test/t/test_tshark.py +++ b/test/t/test_tshark.py @@ -13,9 +13,8 @@ class TestTshark: @pytest.mark.complete("tshark -O foo,htt", require_cmd=True) def test_3(self, completion): - # When there's only one completion, it's be the one with "foo," prefix; - # when multiple (e.g. http and http2), it's the completion alone. - assert completion == "foo,http" or "http" in completion + # p: one completion only; http: e.g. http and http2 + assert completion == "p" or "http" in completion @pytest.mark.complete("tshark -o tcp", require_cmd=True) def test_4(self, completion): @@ -29,3 +28,7 @@ class TestTshark: def test_6(self, completion): """Test there are no URLs in completions.""" assert not any("://" in x for x in completion) + + @pytest.mark.complete("tshark -r ") + def test_input_files(self, completion): + assert completion diff --git a/test/t/test_tsig_keygen.py b/test/t/test_tsig_keygen.py new file mode 100644 index 00000000..8c8a64ac --- /dev/null +++ b/test/t/test_tsig_keygen.py @@ -0,0 +1,12 @@ +import pytest + + +@pytest.mark.bashcomp(cmd="tsig-keygen") +class TestTsigKeygen: + @pytest.mark.complete("tsig-keygen ") + def test_basic(self, completion): + assert not completion + + @pytest.mark.complete("tsig-keygen -", require_cmd=True) + def test_options(self, completion): + assert completion diff --git a/test/t/test_umount.py b/test/t/test_umount.py index dd4ae0b5..2baf0dac 100644 --- a/test/t/test_umount.py +++ b/test/t/test_umount.py @@ -1,7 +1,85 @@ import pytest +from conftest import assert_bash_exec + class TestUmount: + @pytest.fixture(scope="class") + def dummy_mnt(self, request, bash): + """ + umount completion from fstab can't be tested directly because it + (correctly) uses absolute paths. So we create a custom completion which + reads from a file in our text fixture instead. + """ + assert_bash_exec(bash, "unset COMPREPLY cur; unset -f _mnt_completion") + assert_bash_exec( + bash, + "_mnt_completion() { " + "local cur=$(_get_cword); " + "_linux_fstab $(_get_pword) < mount/test-fstab; " + "} && complete -F _mnt_completion _mnt", + ) + request.addfinalizer( + lambda: assert_bash_exec( + bash, "complete -r _mnt; unset -f _mnt_completion" + ) + ) + @pytest.mark.complete("umount ") def test_1(self, completion): assert completion + + @pytest.mark.complete("_mnt /mnt/nice-test-p") + def test_mnt_basic(self, completion, dummy_mnt): + assert completion == "ath" + + # Note in tests below that return only one result, that the result + # is shell unescaped due to how assert_complete handles the + # "one result on same line case". + + @pytest.mark.complete(r"_mnt /mnt/nice\ test-p") + def test_mnt_space(self, completion, dummy_mnt): + assert completion == r"ath" + + @pytest.mark.complete(r"_mnt /mnt/nice\$test-p") + def test_mnt_dollar(self, completion, dummy_mnt): + assert completion == "ath" + + @pytest.mark.complete(r"_mnt /mnt/nice\ test\\p") + def test_mnt_backslash(self, completion, dummy_mnt): + assert completion == "ath" + + @pytest.mark.complete(r"_mnt /mnt/nice\ ") + def test_mnt_after_space(self, completion, dummy_mnt): + assert completion == sorted( + (r"/mnt/nice\ test\\path", r"/mnt/nice\ test-path") + ) + + @pytest.mark.complete(r"_mnt /mnt/nice\$") + def test_mnt_at_dollar(self, completion, dummy_mnt): + assert completion == "test-path" + + @pytest.mark.complete(r"_mnt /mnt/nice\'") + def test_mnt_at_quote(self, completion, dummy_mnt): + assert completion == "test-path" + + @pytest.mark.complete("_mnt /mnt/other") + def test_mnt_other(self, completion, dummy_mnt): + assert completion == r"\'test\ path" + + @pytest.mark.complete("_mnt -L Ubu") + def test_mnt_label_space(self, completion, dummy_mnt): + assert completion == r"ntu\ Karmic" + + @pytest.mark.complete("_mnt -L Deb") + def test_mnt_label_quote(self, completion, dummy_mnt): + assert completion == r"ian-it\'s\ awesome" + + def test_linux_fstab_unescape(self, bash): + assert_bash_exec(bash, r"var=one\'two\\040three\\") + assert_bash_exec(bash, "__linux_fstab_unescape var") + output = assert_bash_exec( + bash, r'printf "%s\n" "$var"', want_output=True + ) + assert output.strip() == "one'two three\\" + assert_bash_exec(bash, "unset var") diff --git a/test/t/test_upgradepkg.py b/test/t/test_upgradepkg.py index 4c72a158..87fe8e4c 100644 --- a/test/t/test_upgradepkg.py +++ b/test/t/test_upgradepkg.py @@ -32,3 +32,20 @@ class TestUpgradepkg: ] ) assert completion == expected + + @pytest.mark.complete("upgradepkg foo%", cwd="slackware/home") + def test_after_percent(self, completion): + expected = sorted( + [ + "%s/" % x + for x in os.listdir("slackware/home") + if os.path.isdir("./slackware/home/%s" % x) + ] + + [ + x + for x in os.listdir("slackware/home") + if os.path.isfile("./slackware/home/%s" % x) + and fnmatch.fnmatch(x, "*.t[bglx]z") + ] + ) + assert completion == ["foo%%%s" % x for x in expected] diff --git a/test/t/test_userdel.py b/test/t/test_userdel.py index 718c6629..3405e127 100644 --- a/test/t/test_userdel.py +++ b/test/t/test_userdel.py @@ -6,6 +6,6 @@ class TestUserdel: def test_1(self, completion): assert completion - @pytest.mark.complete("userdel root") + @pytest.mark.complete("userdel roo") def test_2(self, completion): - assert "root" in completion + assert completion == "t" or "root" in completion diff --git a/test/t/test_valgrind.py b/test/t/test_valgrind.py index c7c979dd..0553b556 100644 --- a/test/t/test_valgrind.py +++ b/test/t/test_valgrind.py @@ -16,13 +16,13 @@ class TestValgrind: @pytest.mark.complete("valgrind --tool=memche", require_cmd=True) def test_3(self, completion): - assert "--tool=memcheck" in completion + assert completion == "ck" or "--tool=memcheck" in completion @pytest.mark.complete( "valgrind --tool=helgrind --history-l", require_cmd=True ) def test_4(self, completion): - assert "--history-level=" in completion + assert completion == "evel=" or "--history-level=" in completion assert not completion.endswith(" ") @pytest.mark.complete(r"valgrind --log-file=v\ 0.log ./bin/", cwd="shared") diff --git a/test/t/test_wol.py b/test/t/test_wol.py index b7a622ee..bf04f76e 100644 --- a/test/t/test_wol.py +++ b/test/t/test_wol.py @@ -5,14 +5,15 @@ import pytest class TestWol: @pytest.mark.complete("wol ") def test_1(self, completion): - assert ( - completion == "00:00:00:00:00:00 11:11:11:11:11:11 " + assert all( + x in completion + for x in "00:00:00:00:00:00 11:11:11:11:11:11 " "22:22:22:22:22:22 33:33:33:33:33:33".split() ) @pytest.mark.complete("wol 00:") def test_2(self, completion): - assert completion == "00:00:00:00:00:00" + assert any(x.endswith("00:00:00:00:00") for x in completion) @pytest.mark.complete("wol -", require_cmd=True) def test_3(self, completion): diff --git a/test/t/test_write.py b/test/t/test_write.py index 8f0886e4..fc4bfa00 100644 --- a/test/t/test_write.py +++ b/test/t/test_write.py @@ -2,6 +2,6 @@ import pytest class TestWrite: - @pytest.mark.complete("write root") + @pytest.mark.complete("write roo") def test_1(self, completion): - assert "root" in completion + assert completion == "t" or "root" in completion diff --git a/test/t/test_xfreerdp.py b/test/t/test_xfreerdp.py index a8435d6c..56162714 100644 --- a/test/t/test_xfreerdp.py +++ b/test/t/test_xfreerdp.py @@ -5,10 +5,20 @@ from conftest import assert_bash_exec class TestXfreerdp: def _help(self, bash): - return assert_bash_exec(bash, "xfreerdp --help || :", want_output=True) + return assert_bash_exec( + bash, "xfreerdp --help 2>&1 || :", want_output=True + ) @pytest.fixture(scope="class") - def slash_syntax(self, bash): + def help_success(self, bash): + output = self._help(bash) + # Example from our CentOS 7 container + # [04:51:31:663] [238:238] [ERROR][com.freerdp.client.x11] - Failed to get pixmap info + if not output or "ERROR" in output.strip().splitlines()[0]: + pytest.skip("--help errored") + + @pytest.fixture(scope="class") + def slash_syntax(self, bash, help_success): if "/help" not in self._help(bash): pytest.skip("Not slash syntax") @@ -18,27 +28,31 @@ class TestXfreerdp: pytest.skip("Not dash syntax") @pytest.mark.complete("xfreerdp /", require_cmd=True) - def test_1(self, bash, completion, slash_syntax): + def test_1(self, bash, completion, help_success, slash_syntax): assert completion @pytest.mark.complete("xfreerdp -", require_cmd=True) - def test_2(self, completion): + def test_2(self, completion, help_success): assert completion @pytest.mark.complete("xfreerdp +", require_cmd=True) - def test_3(self, bash, completion, slash_syntax): + def test_3(self, bash, completion, help_success, slash_syntax): assert completion - @pytest.mark.complete("xfreerdp /kbd:", require_cmd=True) - def test_4(self, bash, completion, slash_syntax): + @pytest.mark.complete( + "xfreerdp /kbd:", + require_cmd=True, + skipif='test -z "$(xfreerdp /kbd-list 2>/dev/null)"', + ) + def test_4(self, bash, completion, help_success, slash_syntax): assert completion @pytest.mark.complete("xfreerdp /help ", require_cmd=True) - def test_5(self, completion): + def test_5(self, completion, help_success): assert not completion @pytest.mark.complete("xfreerdp -k ", require_cmd=True) - def test_6(self, bash, completion, dash_syntax): + def test_6(self, bash, completion, help_success, dash_syntax): assert completion @pytest.mark.complete("xfreerdp --help ", require_cmd=True) diff --git a/test/t/test_xgamma.py b/test/t/test_xgamma.py index beb684f8..151e2d36 100644 --- a/test/t/test_xgamma.py +++ b/test/t/test_xgamma.py @@ -8,5 +8,5 @@ class TestXgamma: @pytest.mark.complete("xgamma -gam", require_cmd=True) def test_2(self, completion): - assert completion == "-gamma" + assert completion == "ma" assert completion.endswith(" ") diff --git a/test/t/test_xhost.py b/test/t/test_xhost.py new file mode 100644 index 00000000..bb2df82a --- /dev/null +++ b/test/t/test_xhost.py @@ -0,0 +1,22 @@ +import pytest + +from conftest import assert_complete, partialize + + +@pytest.mark.bashcomp(pre_cmds=("HOME=$PWD",)) +class TestXhost: + @pytest.mark.parametrize("prefix", ["+", "-", ""]) + def test_hosts(self, bash, hosts, prefix): + completion = assert_complete(bash, "xhost %s" % prefix) + assert completion == ["%s%s" % (prefix, x) for x in hosts] + + @pytest.mark.parametrize("prefix", ["+", "-", ""]) + def test_partial_hosts(self, bash, hosts, prefix): + first_char, partial_hosts = partialize(bash, hosts) + completion = assert_complete(bash, "xhost %s%s" % (prefix, first_char)) + if len(completion) == 1: + assert completion == partial_hosts[0][1:] + else: + assert completion == sorted( + "%s%s" % (prefix, x) for x in partial_hosts + ) diff --git a/test/t/unit/Makefile.am b/test/t/unit/Makefile.am index b96b326c..3eb652af 100644 --- a/test/t/unit/Makefile.am +++ b/test/t/unit/Makefile.am @@ -8,12 +8,15 @@ EXTRA_DIST = \ test_unit_get_cword.py \ test_unit_init_completion.py \ test_unit_ip_addresses.py \ + test_unit_known_hosts_real.py \ test_unit_longopt.py \ test_unit_parse_help.py \ test_unit_parse_usage.py \ test_unit_quote.py \ + test_unit_quote_readline.py \ test_unit_tilde.py \ - test_unit_variables.py + test_unit_variables.py \ + test_unit_xinetd_services.py all: diff --git a/test/t/unit/test_unit_count_args.py b/test/t/unit/test_unit_count_args.py index c0afe736..56bce2cb 100644 --- a/test/t/unit/test_unit_count_args.py +++ b/test/t/unit/test_unit_count_args.py @@ -1,6 +1,6 @@ import pytest -from conftest import assert_bash_exec, TestUnitBase +from conftest import TestUnitBase, assert_bash_exec @pytest.mark.bashcomp( @@ -11,7 +11,7 @@ class TestUnitCountArgs(TestUnitBase): return self._test_unit("_count_args %s; echo $args", *args, **kwargs) def test_1(self, bash): - assert_bash_exec(bash, "_count_args >/dev/null") + assert_bash_exec(bash, "COMP_CWORD= _count_args >/dev/null") def test_2(self, bash): """a b| should set args to 1""" diff --git a/test/t/unit/test_unit_expand.py b/test/t/unit/test_unit_expand.py index 7c0a9836..d2a3ebc4 100644 --- a/test/t/unit/test_unit_expand.py +++ b/test/t/unit/test_unit_expand.py @@ -3,7 +3,7 @@ import pytest from conftest import assert_bash_exec -@pytest.mark.bashcomp(cmd=None) +@pytest.mark.bashcomp(cmd=None, ignore_env=r"^[+-](cur|COMPREPLY)=") class TestUnitExpand: def test_1(self, bash): assert_bash_exec(bash, "_expand >/dev/null") @@ -11,3 +11,21 @@ class TestUnitExpand: def test_2(self, bash): """Test environment non-pollution, detected at teardown.""" assert_bash_exec(bash, "foo() { _expand; }; foo; unset foo") + + def test_user_home_compreply(self, bash, user_home): + user, home = user_home + output = assert_bash_exec( + bash, + r'cur="~%s"; _expand; printf "%%s\n" "$COMPREPLY"' % user, + want_output=True, + ) + assert output.strip() == home + + def test_user_home_cur(self, bash, user_home): + user, home = user_home + output = assert_bash_exec( + bash, + r'cur="~%s/a"; _expand; printf "%%s\n" "$cur"' % user, + want_output=True, + ) + assert output.strip() == "%s/a" % home diff --git a/test/t/unit/test_unit_expand_tilde_by_ref.py b/test/t/unit/test_unit_expand_tilde_by_ref.py index fbc172df..17bdedfe 100644 --- a/test/t/unit/test_unit_expand_tilde_by_ref.py +++ b/test/t/unit/test_unit_expand_tilde_by_ref.py @@ -3,7 +3,7 @@ import pytest from conftest import assert_bash_exec -@pytest.mark.bashcomp(cmd=None) +@pytest.mark.bashcomp(cmd=None, ignore_env=r"^[+-]var=") class TestUnitExpandTildeByRef: def test_1(self, bash): assert_bash_exec(bash, "__expand_tilde_by_ref >/dev/null") @@ -14,3 +14,33 @@ class TestUnitExpandTildeByRef: bash, '_x() { local aa="~"; __expand_tilde_by_ref aa; }; _x; unset _x', ) + + @pytest.mark.parametrize("plain_tilde", (True, False)) + @pytest.mark.parametrize( + "suffix_expanded", + ( + ("", True), + ("/foo", True), + (r"/\$HOME", True), + ("/a b", True), + ("/*", True), + (";echo hello", False), + ("/a;echo hello", True), + ), + ) + def test_expand(self, bash, user_home, plain_tilde, suffix_expanded): + user, home = user_home + suffix, expanded = suffix_expanded + if plain_tilde: + user = "" + if not suffix or not expanded: + home = "~" + elif not expanded: + home = "~%s" % user + output = assert_bash_exec( + bash, + r'var="~%s%s"; __expand_tilde_by_ref var; printf "%%s\n" "$var"' + % (user, suffix), + want_output=True, + ) + assert output.strip() == "%s%s" % (home, suffix.replace(r"\$", "$"),) diff --git a/test/t/unit/test_unit_filedir.py b/test/t/unit/test_unit_filedir.py index 7f14f294..b847efc2 100644 --- a/test/t/unit/test_unit_filedir.py +++ b/test/t/unit/test_unit_filedir.py @@ -1,3 +1,9 @@ +import os +import shutil +import sys +import tempfile +from pathlib import Path + import pytest from conftest import assert_bash_exec, assert_complete @@ -24,102 +30,206 @@ class TestUnitFiledir: "complete -F _fd fd", ) + @pytest.fixture(scope="class") + def non_windows_testdir(self, request, bash): + if sys.platform.startswith("win"): + pytest.skip("Filenames not allowed on Windows") + tempdir = Path(tempfile.mkdtemp(prefix="bash-completion_filedir")) + request.addfinalizer(lambda: shutil.rmtree(str(tempdir))) + subdir = tempdir / 'a"b' + subdir.mkdir() + (subdir / "d").touch() + subdir = tempdir / "a*b" + subdir.mkdir() + (subdir / "j").touch() + subdir = tempdir / r"a\b" + subdir.mkdir() + (subdir / "g").touch() + return tempdir + + @pytest.fixture(scope="class") + def utf8_ctype(self, bash): + # TODO: this likely is not the right thing to do. Instead we should + # grab the setting from the running shell, possibly eval $(locale) + # in a subshell and grab LC_CTYPE from there. That doesn't seem to work + # either everywhere though. + lc_ctype = os.environ.get("LC_CTYPE", "") + if "UTF-8" not in lc_ctype: + pytest.skip("Applicable only in LC_CTYPE=UTF-8 setups") + return lc_ctype + def test_1(self, bash): assert_bash_exec(bash, "_filedir >/dev/null") @pytest.mark.parametrize("funcname", "f f2".split()) def test_2(self, bash, functions, funcname): completion = assert_complete(bash, "%s ab/" % funcname, cwd="_filedir") - assert completion == "ab/e" + assert completion == "e" @pytest.mark.parametrize("funcname", "f f2".split()) def test_3(self, bash, functions, funcname): completion = assert_complete( bash, r"%s a\ b/" % funcname, cwd="_filedir" ) - assert completion == "a b/i" + assert completion == "i" @pytest.mark.parametrize("funcname", "f f2".split()) def test_4(self, bash, functions, funcname): completion = assert_complete( bash, r"%s a\'b/" % funcname, cwd="_filedir" ) - assert completion == "a'b/c" + assert completion == "c" @pytest.mark.parametrize("funcname", "f f2".split()) def test_5(self, bash, functions, funcname): completion = assert_complete( bash, r"%s a\&b/" % funcname, cwd="_filedir" ) - assert completion == "a&b/f" + assert completion == "f" @pytest.mark.parametrize("funcname", "f f2".split()) def test_6(self, bash, functions, funcname): completion = assert_complete( bash, r"%s a\$" % funcname, cwd="_filedir" ) - assert completion == "a$b/" + assert completion == "b/" @pytest.mark.parametrize("funcname", "f f2".split()) def test_7(self, bash, functions, funcname): completion = assert_complete( bash, r"%s 'ab/" % funcname, cwd="_filedir" ) - assert completion == "ab/e" + assert completion == "e'" @pytest.mark.parametrize("funcname", "f f2".split()) def test_8(self, bash, functions, funcname): completion = assert_complete( bash, r"%s 'a b/" % funcname, cwd="_filedir" ) - assert completion == "a b/i" + assert completion == "i'" @pytest.mark.parametrize("funcname", "f f2".split()) def test_9(self, bash, functions, funcname): completion = assert_complete( bash, r"%s 'a$b/" % funcname, cwd="_filedir" ) - assert completion == "a$b/h" + assert completion == "h'" @pytest.mark.parametrize("funcname", "f f2".split()) def test_10(self, bash, functions, funcname): completion = assert_complete( bash, r"%s 'a&b/" % funcname, cwd="_filedir" ) - assert completion == "a&b/f" + assert completion == "f'" @pytest.mark.parametrize("funcname", "f f2".split()) def test_11(self, bash, functions, funcname): completion = assert_complete( bash, r'%s "ab/' % funcname, cwd="_filedir" ) - assert completion == "ab/e" + assert completion == 'e"' @pytest.mark.parametrize("funcname", "f f2".split()) def test_12(self, bash, functions, funcname): completion = assert_complete( bash, r'%s "a b/' % funcname, cwd="_filedir" ) - assert completion == "a b/i" + assert completion == 'i"' @pytest.mark.parametrize("funcname", "f f2".split()) def test_13(self, bash, functions, funcname): completion = assert_complete( bash, "%s \"a'b/" % funcname, cwd="_filedir" ) - assert completion == "a'b/c" + assert completion == 'c"' @pytest.mark.parametrize("funcname", "f f2".split()) def test_14(self, bash, functions, funcname): completion = assert_complete( bash, '%s "a&b/' % funcname, cwd="_filedir" ) - assert completion == "a&b/f" + assert completion == 'f"' @pytest.mark.complete(r"fd a\ ", cwd="_filedir") def test_15(self, functions, completion): - assert completion == "a b/" + assert completion == "b/" @pytest.mark.complete("g ", cwd="_filedir/ext") def test_16(self, functions, completion): assert completion == sorted("ee.e1 foo/ gg.e1 ii.E1".split()) + + @pytest.mark.parametrize("funcname", "f f2".split()) + def test_17(self, bash, functions, funcname): + completion = assert_complete( + bash, r"%s a\$b/" % funcname, cwd="_filedir" + ) + assert completion == "h" + + @pytest.mark.parametrize("funcname", "f f2".split()) + def test_18(self, bash, functions, funcname): + completion = assert_complete( + bash, r"%s \[x" % funcname, cwd="_filedir/brackets" + ) + assert completion == r"\]" + + @pytest.mark.parametrize("funcname", "f f2".split()) + def test_19(self, bash, functions, funcname, non_windows_testdir): + completion = assert_complete( + bash, '%s a\\"b/' % funcname, cwd=non_windows_testdir + ) + assert completion == "d" + + @pytest.mark.parametrize("funcname", "f f2".split()) + def test_20(self, bash, functions, funcname, non_windows_testdir): + completion = assert_complete( + bash, r"%s a\\b/" % funcname, cwd=non_windows_testdir + ) + assert completion == "g" + + @pytest.mark.parametrize("funcname", "f f2".split()) + def test_21(self, bash, functions, funcname, non_windows_testdir): + completion = assert_complete( + bash, "%s 'a\"b/" % funcname, cwd=non_windows_testdir + ) + assert completion == "d'" + + @pytest.mark.parametrize("funcname", "f f2".split()) + def test_22(self, bash, functions, funcname, non_windows_testdir): + completion = assert_complete( + bash, r"%s '%s/a\b/" % (funcname, non_windows_testdir) + ) + assert completion == "g'" + + @pytest.mark.parametrize("funcname", "f f2".split()) + def test_23(self, bash, functions, funcname, non_windows_testdir): + completion = assert_complete( + bash, r'%s "a\"b/' % funcname, cwd=non_windows_testdir + ) + assert completion == 'd"' + + @pytest.mark.parametrize("funcname", "f f2".split()) + def test_24(self, bash, functions, funcname, non_windows_testdir): + completion = assert_complete( + bash, r'%s "a\\b/' % funcname, cwd=non_windows_testdir + ) + assert completion == 'g"' + + @pytest.mark.parametrize("funcname", "f f2".split()) + def test_25(self, bash, functions, funcname): + completion = assert_complete( + bash, r'%s "a\b/' % funcname, cwd="_filedir" + ) + assert completion == '\b\b\bb/e"' + + @pytest.mark.parametrize("funcname", "f f2".split()) + def test_26(self, bash, functions, funcname): + completion = assert_complete( + bash, r'%s "a\$b/' % funcname, cwd="_filedir" + ) + assert completion == 'h"' + + @pytest.mark.xfail(reason="TODO: non-ASCII issues with test suite?") + @pytest.mark.parametrize("funcname", "f f2".split()) + def test_27(self, bash, functions, funcname, utf8_ctype): + completion = assert_complete(bash, "%s aé/" % funcname, cwd="_filedir") + assert completion == "g" diff --git a/test/t/unit/test_unit_get_comp_words_by_ref.py b/test/t/unit/test_unit_get_comp_words_by_ref.py index 1603bad6..b6498fa7 100644 --- a/test/t/unit/test_unit_get_comp_words_by_ref.py +++ b/test/t/unit/test_unit_get_comp_words_by_ref.py @@ -1,16 +1,17 @@ import pytest -from conftest import assert_bash_exec, TestUnitBase +from conftest import TestUnitBase, assert_bash_exec @pytest.mark.bashcomp( - cmd=None, ignore_env=r"^(\+(cur|prev)|[+-]COMP_(WORDS|CWORD|LINE|POINT))=" + cmd=None, + ignore_env=r"^(\+(words|cword|cur|prev)|[+-]COMP_(WORDS|CWORD|LINE|POINT))=", ) class TestUnitGetCompWordsByRef(TestUnitBase): def _test(self, bash, *args, **kwargs): assert_bash_exec(bash, "unset cur prev") output = self._test_unit( - "_get_comp_words_by_ref %s cur prev; echo $cur,$prev", + "_get_comp_words_by_ref %s cur prev; echo $cur,${prev-}", bash, *args, **kwargs @@ -18,7 +19,11 @@ class TestUnitGetCompWordsByRef(TestUnitBase): return output.strip() def test_1(self, bash): - assert_bash_exec(bash, "_get_comp_words_by_ref cur >/dev/null") + assert_bash_exec( + bash, + "COMP_WORDS=() COMP_CWORD= COMP_POINT= COMP_LINE= " + "_get_comp_words_by_ref cur >/dev/null", + ) def test_2(self, bash): """a b|""" @@ -165,3 +170,91 @@ class TestUnitGetCompWordsByRef(TestUnitBase): """a 'b&c|""" output = self._test(bash, '(a "\'b&c")', 1, "a 'b&c", 6) assert output == "'b&c,a" + + def test_30(self, bash): + """a b| to all vars""" + assert_bash_exec(bash, "unset words cword cur prev") + output = self._test_unit( + "_get_comp_words_by_ref words cword cur prev%s; " + 'echo "${words[@]}",$cword,$cur,$prev', + bash, + "(a b)", + 1, + "a b", + 3, + ) + assert output == "a b,1,b,a" + + def test_31(self, bash): + """a b| to alternate vars""" + assert_bash_exec(bash, "unset words2 cword2 cur2 prev2") + output = self._test_unit( + "_get_comp_words_by_ref -w words2 -i cword2 -c cur2 -p prev2%s; " + 'echo $cur2,$prev2,"${words2[@]}",$cword2', + bash, + "(a b)", + 1, + "a b", + 3, + ) + assert output == "b,a,a b,1" + assert_bash_exec(bash, "unset words2 cword2 cur2 prev2") + + def test_32(self, bash): + """a b : c| with wordbreaks -= :""" + assert_bash_exec(bash, "unset words") + output = self._test_unit( + '_get_comp_words_by_ref -n : words%s; echo "${words[@]}"', + bash, + "(a b : c)", + 3, + "a b : c", + 7, + ) + assert output == "a b : c" + + def test_33(self, bash): + """a b: c| with wordbreaks -= :""" + assert_bash_exec(bash, "unset words") + output = self._test_unit( + '_get_comp_words_by_ref -n : words%s; echo "${words[@]}"', + bash, + "(a b : c)", + 3, + "a b: c", + 6, + ) + assert output == "a b: c" + + def test_34(self, bash): + """a b :c| with wordbreaks -= :""" + assert_bash_exec(bash, "unset words") + output = self._test_unit( + '_get_comp_words_by_ref -n : words%s; echo "${words[@]}"', + bash, + "(a b : c)", + 3, + "a b :c", + 6, + ) + assert output == "a b :c" + + def test_35(self, bash): + r"""a b\ :c| with wordbreaks -= :""" + assert_bash_exec(bash, "unset words") + output = self._test_unit( + '_get_comp_words_by_ref -n : words%s; echo "${words[@]}"', + bash, + "(a 'b ' : c)", + 3, + r"a b\ :c", + 7, + ) + assert output == "a b :c" + + def test_unknown_arg_error(self, bash): + with pytest.raises(AssertionError) as ex: + _ = assert_bash_exec( + bash, "_get_comp_words_by_ref dummy", want_output=True + ) + ex.match("dummy.* unknown argument") diff --git a/test/t/unit/test_unit_get_cword.py b/test/t/unit/test_unit_get_cword.py index 3042dd29..0b56d163 100644 --- a/test/t/unit/test_unit_get_cword.py +++ b/test/t/unit/test_unit_get_cword.py @@ -1,17 +1,22 @@ +import pexpect import pytest -from conftest import assert_bash_exec, TestUnitBase +from conftest import PS1, TestUnitBase, assert_bash_exec @pytest.mark.bashcomp( - cmd=None, ignore_env=r"^[+-]COMP_(WORDS|CWORD|LINE|POINT)=" + cmd=None, ignore_env=r"^[+-](COMP_(WORDS|CWORD|LINE|POINT)|_scp_path_esc)=" ) class TestUnitGetCword(TestUnitBase): def _test(self, *args, **kwargs): return self._test_unit("_get_cword %s; echo", *args, **kwargs) def test_1(self, bash): - assert_bash_exec(bash, "_get_cword >/dev/null") + assert_bash_exec( + bash, + "COMP_WORDS=() COMP_CWORD= COMP_LINE= COMP_POINT= " + "_get_cword >/dev/null", + ) def test_2(self, bash): """a b| should return b""" @@ -133,3 +138,17 @@ class TestUnitGetCword(TestUnitBase): """a 'b&c| should return 'b&c""" output = self._test(bash, '(a "\'b&c")', 1, "a 'b&c", 6) assert output == "'b&c" + + @pytest.mark.xfail(reason="TODO: non-ASCII issues with test suite?") + def test_24(self, bash): + """Index shouldn't drop below 0""" + bash.send("scp ääää§ se\t\r\n") + got = bash.expect_exact( + [ + "index: substring expression < 0", + PS1, + pexpect.EOF, + pexpect.TIMEOUT, + ] + ) + assert got == 1 diff --git a/test/t/unit/test_unit_init_completion.py b/test/t/unit/test_unit_init_completion.py index 64f3b511..64a5a790 100644 --- a/test/t/unit/test_unit_init_completion.py +++ b/test/t/unit/test_unit_init_completion.py @@ -1,6 +1,6 @@ import pytest -from conftest import assert_bash_exec, TestUnitBase +from conftest import TestUnitBase, assert_bash_exec, assert_complete @pytest.mark.bashcomp( @@ -13,12 +13,22 @@ class TestUnitInitCompletion(TestUnitBase): """Test environment non-pollution, detected at teardown.""" assert_bash_exec( bash, - "foo() { local cur prev words cword; _init_completion; }; " + "foo() { " + "local cur prev words cword " + "COMP_WORDS=() COMP_CWORD=0 COMP_LINE= COMP_POINT=0; " + "_init_completion; }; " "foo; unset foo", ) def test_2(self, bash): output = self._test_unit( - "_init_completion %s; echo $cur,$prev", bash, "(a)", 0, "a", 0 + "_init_completion %s; echo $cur,${prev-}", bash, "(a)", 0, "a", 0 ) assert output == "," + + @pytest.mark.parametrize("redirect", "> >> 2> < &>".split()) + def test_redirect(self, bash, redirect): + completion = assert_complete( + bash, "%s " % redirect, cwd="shared/default" + ) + assert all(x in completion for x in "foo bar".split()) diff --git a/test/t/unit/test_unit_known_hosts_real.py b/test/t/unit/test_unit_known_hosts_real.py new file mode 100644 index 00000000..ac5205e1 --- /dev/null +++ b/test/t/unit/test_unit_known_hosts_real.py @@ -0,0 +1,158 @@ +from itertools import chain + +import pytest + +from conftest import assert_bash_exec + + +@pytest.mark.bashcomp( + cmd=None, + ignore_env="^[+-](COMP(REPLY|_KNOWN_HOSTS_WITH_HOSTFILE)|OLDHOME)=", +) +class TestUnitKnownHostsReal: + @pytest.mark.parametrize( + "prefix,colon_flag,hostfile", + [("", "", True), ("", "", False), ("user@", "c", True)], + ) + def test_basic( + self, bash, hosts, avahi_hosts, prefix, colon_flag, hostfile + ): + expected = ( + "%s%s%s" % (prefix, x, ":" if colon_flag else "") + for x in chain( + hosts if hostfile else avahi_hosts, + # fixtures/_known_hosts_real/config + "gee hus jar #not-a-comment".split(), + # fixtures/_known_hosts_real/known_hosts + ( + "doo", + "ike", + "jub", + "10.0.0.1", + "kyl", + "100.0.0.2", + "10.10.0.3", + "blah", + "fd00:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:5555", + "fe80::123:0xff:dead:beef%eth0", + "1111:2222:3333:4444:5555:6666:xxxx:abab", + "11xx:2222:3333:4444:5555:6666:xxxx:abab", + "::42", + ), + ) + ) + assert_bash_exec( + bash, + "unset -v COMP_KNOWN_HOSTS_WITH_HOSTFILE" + if hostfile + else "COMP_KNOWN_HOSTS_WITH_HOSTFILE=", + ) + output = assert_bash_exec( + bash, + "_known_hosts_real -a%sF _known_hosts_real/config '%s'; " + r'printf "%%s\n" "${COMPREPLY[@]}"; unset COMPREPLY' + % (colon_flag, prefix), + want_output=True, + ) + assert sorted(set(output.split())) == sorted(expected) + + @pytest.mark.parametrize( + "family,result", + ( + ("4", "127.0.0.1 localhost"), + ("6", "::1 localhost"), + ("46", "localhost"), + ), + ) + def test_ip_filtering(self, bash, family, result): + assert_bash_exec( + bash, "unset -v COMPREPLY COMP_KNOWN_HOSTS_WITH_HOSTFILE" + ) + output = assert_bash_exec( + bash, + "COMP_KNOWN_HOSTS_WITH_HOSTFILE= " + "_known_hosts_real -%sF _known_hosts_real/localhost_config ''; " + r'printf "%%s\n" "${COMPREPLY[@]}"' % family, + want_output=True, + ) + assert sorted(set(output.strip().split())) == sorted(result.split()) + + def test_consecutive_spaces(self, bash, hosts): + expected = hosts.copy() + # fixtures/_known_hosts_real/spaced conf + expected.extend("gee hus #not-a-comment".split()) + # fixtures/_known_hosts_real/known_hosts2 + expected.extend("two two2 two3 two4".split()) + # fixtures/_known_hosts_/spaced known_hosts + expected.extend("doo ike".split()) + + output = assert_bash_exec( + bash, + "unset -v COMPREPLY COMP_KNOWN_HOSTS_WITH_HOSTFILE; " + "_known_hosts_real -aF '_known_hosts_real/spaced conf' ''; " + r'printf "%s\n" "${COMPREPLY[@]}"', + want_output=True, + ) + assert sorted(set(output.strip().split())) == sorted(expected) + + def test_files_starting_with_tilde(self, bash, hosts): + expected = hosts.copy() + # fixtures/_known_hosts_real/known_hosts2 + expected.extend("two two2 two3 two4".split()) + # fixtures/_known_hosts_real/known_hosts3 + expected.append("three") + # fixtures/_known_hosts_real/known_hosts4 + expected.append("four") + + assert_bash_exec(bash, 'OLDHOME="$HOME"; HOME="%s"' % bash.cwd) + output = assert_bash_exec( + bash, + "unset -v COMPREPLY COMP_KNOWN_HOSTS_WITH_HOSTFILE; " + "_known_hosts_real -aF _known_hosts_real/config_tilde ''; " + r'printf "%s\n" "${COMPREPLY[@]}"', + want_output=True, + ) + assert_bash_exec(bash, 'HOME="$OLDHOME"') + assert sorted(set(output.strip().split())) == sorted(expected) + + def test_included_configs(self, bash, hosts): + expected = hosts.copy() + # fixtures/_known_hosts_real/config_include_recursion + expected.append("recursion") + # fixtures/_known_hosts_real/.ssh/config_relative_path + expected.append("relative_path") + # fixtures/_known_hosts_real/.ssh/config_asterisk_* + expected.extend("asterisk_1 asterisk_2".split()) + # fixtures/_known_hosts_real/.ssh/config_question_mark + expected.append("question_mark") + + assert_bash_exec( + bash, 'OLDHOME="$HOME"; HOME="%s/_known_hosts_real"' % bash.cwd + ) + output = assert_bash_exec( + bash, + "unset -v COMPREPLY COMP_KNOWN_HOSTS_WITH_HOSTFILE; " + "_known_hosts_real -aF _known_hosts_real/config_include ''; " + r'printf "%s\n" "${COMPREPLY[@]}"', + want_output=True, + ) + assert_bash_exec(bash, 'HOME="$OLDHOME"') + assert sorted(set(output.strip().split())) == sorted(expected) + + def test_no_globbing(self, bash): + assert_bash_exec( + bash, 'OLDHOME="$HOME"; HOME="%s/_known_hosts_real"' % bash.cwd + ) + output = assert_bash_exec( + bash, + "cd _known_hosts_real; " + "unset -v COMPREPLY COMP_KNOWN_HOSTS_WITH_HOSTFILE; " + "_known_hosts_real -aF config ''; " + r'printf "%s\n" "${COMPREPLY[@]}"; ' + "cd - &>/dev/null", + want_output=True, + ) + assert_bash_exec(bash, 'HOME="$OLDHOME"') + completion = sorted(set(output.strip().split())) + assert "gee" in completion + assert "gee-filename-canary" not in completion diff --git a/test/t/unit/test_unit_longopt.py b/test/t/unit/test_unit_longopt.py index ac0ac836..c5488e34 100644 --- a/test/t/unit/test_unit_longopt.py +++ b/test/t/unit/test_unit_longopt.py @@ -11,6 +11,8 @@ class TestUnitLongopt: def functions(self, request, bash): assert_bash_exec(bash, "_grephelp() { cat _longopt/grep--help.txt; }") assert_bash_exec(bash, "complete -F _longopt _grephelp") + assert_bash_exec(bash, "_various() { cat _longopt/various.txt; }") + assert_bash_exec(bash, "complete -F _longopt _various") @pytest.mark.complete("_grephelp --") def test_1(self, functions, completion): @@ -32,3 +34,19 @@ class TestUnitLongopt: assert completion assert any(x.endswith("=") for x in completion) assert any(not x.endswith("=") for x in completion) + + @pytest.mark.complete("_various --") + def test_no_dashdashdash(self, functions, completion): + assert all(not x.startswith("---") for x in completion) + + @pytest.mark.complete("_various --") + def test_no_trailingdash(self, functions, completion): + assert all(not x.endswith("-") for x in completion) + + @pytest.mark.complete("_various --") + def test_underscore(self, functions, completion): + assert "--foo_bar" in completion + + @pytest.mark.complete("_various --") + def test_equals(self, functions, completion): + assert "--foo=" in completion diff --git a/test/t/unit/test_unit_quote.py b/test/t/unit/test_unit_quote.py index e9f81c2d..b280bd68 100644 --- a/test/t/unit/test_unit_quote.py +++ b/test/t/unit/test_unit_quote.py @@ -1,6 +1,6 @@ import pytest -from conftest import assert_bash_exec, TestUnitBase +from conftest import TestUnitBase, assert_bash_exec @pytest.mark.bashcomp(cmd=None) diff --git a/test/t/unit/test_unit_quote_readline.py b/test/t/unit/test_unit_quote_readline.py new file mode 100644 index 00000000..e2b437e3 --- /dev/null +++ b/test/t/unit/test_unit_quote_readline.py @@ -0,0 +1,15 @@ +import pytest + +from conftest import assert_bash_exec + + +@pytest.mark.bashcomp(cmd=None) +class TestUnitQuoteReadline: + def test_exec(self, bash): + assert_bash_exec(bash, "quote_readline '' >/dev/null") + + def test_env_non_pollution(self, bash): + """Test environment non-pollution, detected at teardown.""" + assert_bash_exec( + bash, "foo() { quote_readline meh >/dev/null; }; foo; unset foo" + ) diff --git a/test/t/unit/test_unit_variables.py b/test/t/unit/test_unit_variables.py index dd7a4219..d62bc4a4 100644 --- a/test/t/unit/test_unit_variables.py +++ b/test/t/unit/test_unit_variables.py @@ -18,11 +18,11 @@ class TestUnitVariables: @pytest.mark.complete(": $___v") def test_simple_variable_name(self, functions, completion): - assert completion == "$___var".split() + assert completion == "ar" @pytest.mark.complete(": ${assoc1[") def test_single_array_index(self, functions, completion): - assert completion == "${assoc1[idx]}".split() + assert completion == "idx]}" @pytest.mark.complete(": ${assoc2[") def test_multiple_array_indexes(self, functions, completion): @@ -30,12 +30,12 @@ class TestUnitVariables: @pytest.mark.complete(": ${assoc1[bogus]") def test_closing_curly_after_square(self, functions, completion): - assert completion == "${assoc1[bogus]}".split() + assert completion == "}" @pytest.mark.complete(": ${assoc1[@") def test_closing_brackets_after_at(self, functions, completion): - assert completion == "${assoc1[@]}".split() + assert completion == "]}" @pytest.mark.complete(": ${#___v") def test_hash_prefix(self, functions, completion): - assert completion == "${#___var}".split() + assert completion == "ar}" diff --git a/test/t/unit/test_unit_xinetd_services.py b/test/t/unit/test_unit_xinetd_services.py new file mode 100644 index 00000000..7a90cb7f --- /dev/null +++ b/test/t/unit/test_unit_xinetd_services.py @@ -0,0 +1,22 @@ +import pytest + +from conftest import assert_bash_exec + + +@pytest.mark.bashcomp(cmd=None, ignore_env=r"^\+COMPREPLY=") +class TestUnitXinetdServices: + def test_direct(self, bash): + assert_bash_exec(bash, "_xinetd_services >/dev/null") + + def test_env_non_pollution(self, bash): + """Test environment non-pollution, detected at teardown.""" + assert_bash_exec(bash, "foo() { _xinetd_services; }; foo; unset foo") + + def test_basic(self, bash): + output = assert_bash_exec( + bash, + "foo() { local BASHCOMP_XINETDDIR=$PWD/shared/bin;unset COMPREPLY; " + '_xinetd_services; printf "%s\\n" "${COMPREPLY[@]}"; }; foo; unset foo', + want_output=True, + ) + assert sorted(output.split()) == ["arp", "ifconfig"] diff --git a/test/test-cmd-list.txt b/test/test-cmd-list.txt new file mode 100644 index 00000000..eb8398e0 --- /dev/null +++ b/test/test-cmd-list.txt @@ -0,0 +1,687 @@ +2to3 +7z +a2ps +a2x +abook +aclocal +acpi +acroread +adb +add_members +alias +alpine +animate +ant +apache2ctl +appdata-validate +apt-build +apt-cache +apt-get +aptitude +arch +arp +arping +arpspoof +asciidoc +aspell +autoconf +autoheader +automake +autoreconf +autorpm +autoscan +autossh +autoupdate +avctrl +awk +badblocks +base64 +bash +bc +/bin/chroot +bind +/bin/rmdir +bison +bk +bmake +brctl +bsdtar +btdownloadcurses.py +btdownloadgui.py +btdownloadheadless.py +bts +bzip2 +c++ +cal +cancel +cardctl +carton +cat +cc +ccache +ccze +cd +cdrecord +cfagent +cfrun +chage +change_pw +check_db +check_perms +checksec +chfn +chgrp +chkconfig +chmod +chown +chpasswd +chromium-browser +chronyc +chroot +chrpath +chsh +ci +ciptool +civclient +civserver +cksfv +cleanarch +clisp +clone_member +co +colordiff +compare +compgen +complete +composite +config_list +configure +conjure +convert +cowsay +cp +cpan2dist +cpio +cppcheck +createdb +createuser +crontab +cryptsetup +csplit +curl +cut +cvs +cvsps +date +dcop +dd +declare +deja-dup +desktop-file-validate +df +dfutool +dhclient +dict +diff +dir +display +dmesg +dmypy +dnssec-keygen +dnsspoof +dot +dpkg +dpkg-deb +dpkg-query +dpkg-reconfigure +dpkg-source +dropdb +dropuser +dselect +dsniff +du +dumpdb +dumpe2fs +e2freefrag +e2label +ebtables +ecryptfs-migrate-home +eject +enscript +env +eog +etherwake +ether-wake +evince +expand +explodepkg +export +faillog +fbgs +fbi +feh +file +filefrag +file-roller +filesnarf +find +find_member +finger +fio +firefox +flake8 +fmt +fold +freebsd-update +freeciv +freeciv-server +function +fusermount +g++ +g4 +g77 +gcc +gcj +gcl +gdb +genaliases +gendiff +genisoimage +geoiplookup +getconf +getent +gkrellm +gm +gmplayer +gnatmake +gnokii +gnome-mplayer +gnome-screenshot +gpasswd +gpc +gperf +gpg +gpg2 +gpgv +gphoto2 +gprof +grep +groupadd +groupdel +groupmems +groupmod +growisofs +grpck +grub +gssdp-discover +gzip +hciattach +hciconfig +hcitool +hddtemp +head +hexdump +hid2hci +host +hostname +hping2 +hping3 +htop +htpasswd +hunspell +hwclock +iconv +id +identify +idn +ifdown +ifstat +iftop +ifup +import +influx +info +inject +inotifywait +inotifywatch +insmod +installpkg +interdiff +invoke-rc.d +ionice +ip +ipcalc +iperf +iperf3 +ipmitool +ipsec +iptables +ipv6calc +irb +iscsiadm +isort +isql +iwconfig +iwlist +iwpriv +iwspy +jar +jarsigner +java +javac +javadoc +javaws +jpegoptim +jps +jq +jshint +jsonschema +json_xs +k3b +kcov +kdvi +kill +killall +kldload +kldunload +koji +kpdf +kplayer +ktutil +l2ping +larch +lastlog +ld +ldapadd +ldapcompare +ldapdelete +ldapmodrdn +ldappasswd +ldapsearch +ldapvi +ldapwhoami +ldd +less +lftp +lftpget +lilo +links +lintian +lintian-info +lisp +list_admins +list_lists +list_members +list_owners +ln +locale-gen +look +lpq +lpr +lrzip +ls +lsof +lspci +lsscsi +lsusb +lua +luac +luseradd +luserdel +lusermod +lvchange +lvcreate +lvdisplay +lvextend +lvm +lvmdiskscan +lvreduce +lvremove +lvrename +lvresize +lvs +lvscan +lz4 +lzip +lzma +lzop +m4 +macof +mailmanctl +mailsnarf +make +makepkg +man +mc +mcrypt +md5sum +mdadm +mdecrypt +mdtool +medusa +mencoder +mii-diag +mii-tool +minicom +mkdir +mkfifo +mkinitrd +mkisofs +mknod +mktemp +mmsitepass +mock +modinfo +modprobe +module +mogrify +monodevelop +montage +mount +mplayer +mr +msgsnarf +msynctool +mtx +munindoc +munin-node-configure +munin-run +mussh +mutt +muttng +mv +mypy +mysql +mysqladmin +nc +ncftp +nethogs +netstat +newgrp +newlist +newusers +ngrep +nl +nm +nmap +nmcli +nproc +nslookup +nsupdate +ntpdate +objcopy +objdump +od +oggdec +op +openssl +opera +optipng +p4 +pack200 +passwd +paste +patch +pdftotext +perl +perlcritic +perldoc +perltidy +pgrep +phing +pidof +pine +pinfo +ping +pkgadd +pkg-config +pkg_deinstall +pkg_delete +pkg-get +pkg_info +pkgrm +pkgtool +pkgutil +pkill +plague-client +pm-hibernate +pm-is-supported +pm-powersave +pngfix +portinstall +portsnap +portupgrade +postcat +postconf +postfix +postmap +postsuper +povray +pr +prelink +printenv +protoc +psql +ptx +puppet +pushd +pv +pvchange +pvcreate +pvdisplay +pvmove +pvremove +pvs +pvscan +pwck +pwd +pwdx +pwgen +pycodestyle +pydoc +pydocstyle +pyflakes +pylint +pylint-3 +pytest +python +python3 +pyvenv +qemu +qrunner +querybts +quota +quotacheck +quotaon +radvdump +rcs +rcsdiff +rdesktop +rdict +readelf +readonly +remove_members +removepkg +renice +repomanage +reportbug +reptyr +resolvconf +rfcomm +rfkill +ri +rlog +rm +rmdir +rmlist +rmmod +route +rpcdebug +rpm +rpm2tgz +rpmbuild +rrdtool +rsync +rtcwake +runuser +sbcl +sbcl-mt +sbopkg +scp +screen +scrub +sdptool +secret-tool +sed +seq +service +set +setquota +sftp +sh +sha1sum +shar +shellcheck +sitecopy +slackpkg +slapt-get +slapt-src +smartctl +smbcacls +smbclient +smbcquotas +smbget +smbpasswd +smbtar +smbtree +snownews +sort +split +spovray +sqlite3 +ss +ssh +ssh-add +ssh-copy-id +sshfs +ssh-keygen +sshmitm +sshow +strace +stream +strings +strip +su +sudo +sum +svcadm +svk +svn +svnadmin +svnlook +synclient +sync_members +sysbench +sysctl +tac +tail +tar +tcpdump +tcpkill +tcpnice +tee +texindex +tightvncviewer +time +timeout +tipc +totem +touch +tox +tr +tracepath +tshark +tsig-keygen +tune2fs +udevadm +ulimit +umount +unace +uname +unexpand +uniq +units +unpack200 +unrar +unset +unshunt +update-alternatives +update-rc.d +upgradepkg +urlsnarf +uscan +useradd +userdel +usermod +valgrind +vdir +vgcfgbackup +vgcfgrestore +vgchange +vgck +vgconvert +vgcreate +vgdisplay +vgexport +vgextend +vgimport +vgmerge +vgmknodes +vgreduce +vgremove +vgrename +vgs +vgscan +vgsplit +vi +vipw +vmstat +vncviewer +vpnc +watch +wc +webmitm +wget +who +wine +withlist +wodim +wol +write +wsimport +wtf +wvdial +xdg-mime +xdg-settings +xfreerdp +xgamma +xm +xmllint +xmlwf +xmms +xmodmap +xpovray +xrandr +xrdb +xsltproc +xvfb-run +xvnc4viewer +xxd +xz +xzdec +ypcat +ypmatch +yum +yum-arch +zopfli +zopflipng diff --git a/test/unit/__expand_tilde_by_ref.exp b/test/unit/__expand_tilde_by_ref.exp deleted file mode 100644 index 5569fe37..00000000 --- a/test/unit/__expand_tilde_by_ref.exp +++ /dev/null @@ -1,65 +0,0 @@ -# @param string $out Reference to variable to hold value of bash environment -# variable $HOME. -proc setup {home user} { - upvar $home _home - upvar $user _user - save_env - assert_bash_exec {echo "$HOME"} {} /@ _home - set _home [string trim $_home] - assert_bash_exec {id -un 2>/dev/null || echo "$USER"} {} /@ _user - set _user [string trim $_user] -} - - -proc teardown {} { - assert_env_unmodified { - /var=/d - } -} - - -setup home user - - -set test "~user should return $home" -set cmd [format {var="~%s"; __expand_tilde_by_ref var; printf "%%s\n" "$var"} $user] -assert_bash_list "$home" $cmd $test -sync_after_int - -set test "~/foo should return $home/foo" -set cmd {var='~/foo'; __expand_tilde_by_ref var; printf "%s\n" "$var"} -assert_bash_list "$home/foo" $cmd $test -sync_after_int - -set test "~user/bar should return $home/bar" -set cmd [format {var="~%s/bar"; __expand_tilde_by_ref var; printf "%%s\n" "$var"} $user] -assert_bash_list "$home/bar" $cmd $test -sync_after_int - -set test "~user/\$HOME should return $home/\$HOME" -set cmd [format {var="~%s/\$HOME"; __expand_tilde_by_ref var; printf "%%s\n" "$var"} $user] -assert_bash_list "$home/\$HOME" $cmd $test -sync_after_int - -set test "'~user/a b' should return '$home/a b'" -set cmd [format {var="~%s/a b"; __expand_tilde_by_ref var; printf "%%s\n" "$var"} $user] -assert_bash_list [list [format {%s/a b} $home]] $cmd $test -sync_after_int - -set test "~user/* should return $home/*" -set cmd [format {var="~%s/*"; __expand_tilde_by_ref var; printf "%%s\n" "$var"} $user] -assert_bash_list "$home/\*" $cmd $test -sync_after_int - -set test "'~user;echo hello' should return '~user;echo hello' (not expanded)" -set cmd [format {var="~%s;echo hello"; __expand_tilde_by_ref var; printf "%%s\n" "$var"} $user] -assert_bash_list [format "~%s;echo hello" $user] $cmd $test -sync_after_int - -set test "'~user/a;echo hello' should return '$home/a;echo hello'" -set cmd [format {var="~%s/a;echo hello"; __expand_tilde_by_ref var; printf "%%s\n" "$var"} $user] -assert_bash_list "$home/a;echo hello" $cmd $test -sync_after_int - - -teardown diff --git a/test/unit/_expand.exp b/test/unit/_expand.exp deleted file mode 100644 index 59dbed58..00000000 --- a/test/unit/_expand.exp +++ /dev/null @@ -1,33 +0,0 @@ -proc setup {home user} { - upvar $home _home - upvar $user _user - save_env - assert_bash_exec {echo "$HOME"} {} /@ _home - set _home [string trim $_home] - assert_bash_exec {id -un 2>/dev/null || echo "$USER"} {} /@ _user - set _user [string trim $_user] -} - -proc teardown {} { - assert_env_unmodified { - /COMPREPLY=/d - /cur=/d - } -} - - -setup home user - - -set test "~$user should set $home to COMPREPLY" -set cmd [format {cur="~%s" ; _expand ; printf "%%s\n" "$COMPREPLY"} $user] -assert_bash_list "$home" $cmd $test -sync_after_int - -set test "~$user/a should set $home/a to cur" -set cmd [format {cur="~%s/a" ; _expand ; printf "%%s\n" "$cur"} $user] -assert_bash_list "$home/a" $cmd $test -sync_after_int - - -teardown diff --git a/test/unit/_filedir.exp b/test/unit/_filedir.exp deleted file mode 100644 index 1de49504..00000000 --- a/test/unit/_filedir.exp +++ /dev/null @@ -1,124 +0,0 @@ -proc setup {} { - assert_bash_exec {unset COMPREPLY cur} - assert_bash_exec {unset -f _f} - save_env - # Declare bash completion function `_f' - assert_bash_exec { \ - _f() { local cur=$(_get_cword); unset COMPREPLY; _filedir; }; \ - complete -F _f f \ - } - # Declare bash completion function `_f2' with `-o filenames' active. - assert_bash_exec { \ - complete -F _f -o filenames f2 \ - } - # Create directories `a"b', `a*b', and `a\b' only when not running on - # Cygwin/Windows (`"', `*', or `\' aren't allowed in filenames there) - if {! [is_cygwin]} { - # Create directory `a"b' - assert_bash_exec {(cd $TESTDIR/tmp && [ ! -d a\"b ] && mkdir a\"b && touch a\"b/d || true)} - # Create directory `a*b' - assert_bash_exec {(cd $TESTDIR/tmp && [ ! -d a\*b ] && mkdir a\*b && touch a\*b/j || true)} - # Create directory `a\b' - assert_bash_exec {(cd $TESTDIR/tmp && [ ! -d a\\b ] && mkdir a\\b && touch a\\b/g || true)} - } -} - - -proc teardown {} { - if {! [is_cygwin]} { - assert_bash_exec {(cd $TESTDIR/tmp && rm -- a\"b/d && rmdir a\"b/ || true)} - assert_bash_exec {(cd $TESTDIR/tmp && rm -- a\\b/g && rmdir a\\b/ || true)} - assert_bash_exec {(cd $TESTDIR/tmp && rm -- a\*b/j && rmdir a\*b/ || true)} - } - assert_bash_exec {unset COMPREPLY cur} - assert_bash_exec {unset -f _f} - assert_bash_exec {complete -r f} - assert_env_unmodified { - /OLDPWD/d - /OLD_CTYPE/d - } -} - - -setup - - -foreach name {f f2} { - - set test "completing $name a\\\$b/ should return h" - if {[info exists ::env(CI)] && [info exists ::env(DIST)] && $::env(DIST) == "centos6"} { - xfail $test - } else { - set cmd "$name a\\\$b/" - assert_complete_dir h $cmd "$::srcdir/fixtures/_filedir" $test - sync_after_int - } - - if {! [is_cygwin]} { # Illegal characters in file/dir names - set test "completing $name a\\\"b/ should return d"; #" - set cmd "$name a\\\"b/"; #" - assert_complete_dir d $cmd "$TESTDIR/tmp" $test - sync_after_int - - set test "completing $name a\\\\b/ should return g" - set cmd "$name a\\\\b/" - assert_complete_dir g $cmd "$TESTDIR/tmp" $test - sync_after_int - } - - if {! [is_cygwin]} { # Illegal characters in file/dir names - set cmd "$name 'a\"b/"; #" - assert_complete_dir {d'} $cmd "$TESTDIR/tmp" - sync_after_int - - set cmd "$name '$TESTDIR/tmp/a\\b/" - assert_complete_dir {g'} $cmd "$TESTDIR/tmp" - sync_after_int - } - - if {! [is_cygwin]} { # Illegal characters in file/dir names - set cmd "$name \"a\\\"b/"; #" - assert_complete_dir {d"} $cmd "$TESTDIR/tmp"; #" - sync_after_int - } - - if {[info exists ::env(CI)] && [info exists ::env(DIST)] && $::env(DIST) == "centos6"} { - xfail "$name \"a\\\$b/ should show completions" - } else { - set cmd "$name \"a\\\$b/"; #" - assert_complete_dir {h"} $cmd "$::srcdir/fixtures/_filedir"; #" - sync_after_int - } - - set cmd "$name \"a\\b/"; #" - assert_complete_dir "\b\b\bb/e\\\"" $cmd "$::srcdir/fixtures/_filedir" - sync_after_int - - set cmd "$name \"a\\\\b/"; #" - assert_complete_dir {g"} $cmd "$TESTDIR/tmp"; #" - sync_after_int - - set cmd "$name \\\[x" - assert_complete_dir {\[x\]} $cmd "$::srcdir/fixtures/_filedir/brackets" - sync_after_int - -}; # foreach - -set test "completing f aé should return g" -# Execute this test only with LC_CTYPE matching *UTF-8* -# See also: http://www.mail-archive.com/bash-completion-devel\ -# @lists.alioth.debian.org/msg02265.html -# Don't execute this test on expect-5.44 cause it will segfault -# See also: Alioth #312792 -if { - [string first "UTF-8" $::LC_CTYPE] != -1 && - [string first 5.44 [exp_version]] != 0 -} { - assert_complete_dir g "f aé/" "$::srcdir/fixtures/_filedir" -} else { - unsupported "$test" -} -sync_after_int - - -teardown diff --git a/test/unit/_get_comp_words_by_ref.exp b/test/unit/_get_comp_words_by_ref.exp deleted file mode 100644 index a0e1886b..00000000 --- a/test/unit/_get_comp_words_by_ref.exp +++ /dev/null @@ -1,91 +0,0 @@ -proc setup {} { - assert_bash_exec {unset COMP_CWORD COMP_LINE COMP_POINT COMP_WORDS} - save_env -} - - -proc teardown {} { - assert_bash_exec { \ - unset COMP_CWORD COMP_LINE COMP_POINT COMP_WORDS cur prev words cword \ - cur2 prev2 words2 cword2 \ - } - # Delete 'COMP_WORDBREAKS' occupying two lines - assert_env_unmodified { - /COMP_WORDBREAKS=/{N - d - } - } -} - - -setup - - -# See also ./lib/completions/alias.exp. Here `_get_cword' is actually tested -# by moving the cursor left into the current word. - -set test {unknown argument should raise error} -set cmd {_get_comp_words_by_ref dummy} -assert_bash_list {"bash_completion: _get_comp_words_by_ref: `dummy': unknown argument"} $cmd $test -sync_after_int - -set test "a b| to all vars"; # | = cursor position -set cmd {COMP_WORDS=(a b); COMP_CWORD=1; COMP_LINE='a b'; COMP_POINT=3} -assert_bash_exec $cmd -set cmd { \ - _get_comp_words_by_ref words cword prev cur; echo "${words[@]} $cword $cur $prev" \ -} -assert_bash_list {"a b 1 b a"} $cmd $test -sync_after_int - -set test "a b| to alternate vars"; # | = cursor position -set cmd {COMP_WORDS=(a b); COMP_CWORD=1; COMP_LINE='a b'; COMP_POINT=3;} -assert_bash_exec $cmd -set cmd {_get_comp_words_by_ref -c cur2 -p prev2 -w words2 -i cword2} -assert_bash_exec $cmd -set cmd {echo "$cur2 $prev2 ${words2[@]} $cword2"} -assert_bash_list {"b a a b 1"} $cmd $test -sync_after_int - -set test "a b| to alternate vars"; # | = cursor position -set cmd {COMP_WORDS=(a b); COMP_CWORD=1; COMP_LINE='a b'; COMP_POINT=3;} -assert_bash_exec $cmd -set cmd {_get_comp_words_by_ref -c cur2 -p prev2 -w words2 -i cword2} -assert_bash_exec $cmd -set cmd {echo "$cur2 $prev2 ${words2[@]} $cword2"} -assert_bash_list {"b a a b 1"} $cmd $test -sync_after_int - -set test {a b : c| with WORDBREAKS -= :}; # | = cursor position -set cmd {COMP_WORDS=(a b : c); COMP_CWORD=3; COMP_LINE='a b : c'; COMP_POINT=7} -assert_bash_exec $cmd $test -set cmd {_get_comp_words_by_ref -n : words; echo "${words[@]}"} -assert_bash_list {"a b : c"} $cmd $test -sync_after_int - -set test {a b: c| with WORDBREAKS -= :}; # | = cursor position -set cmd {COMP_WORDS=(a b : c); COMP_CWORD=3} -append cmd {; COMP_LINE='a b: c'; COMP_POINT=6} -assert_bash_exec $cmd $test -set cmd {_get_comp_words_by_ref -n : words; echo "${words[@]}"} -assert_bash_list {"a b: c"} $cmd $test -sync_after_int - -set test {a b :c| with WORDBREAKS -= :}; # | = cursor position -set cmd {COMP_WORDS=(a b : c); COMP_CWORD=3} -append cmd {; COMP_LINE='a b :c'; COMP_POINT=6} -assert_bash_exec $cmd $test -set cmd {_get_comp_words_by_ref -n : words; echo "${words[@]}"} -assert_bash_list {"a b :c"} $cmd $test -sync_after_int - -set test {a b\ :c| with WORDBREAKS -= :}; # | = cursor position -set cmd {COMP_WORDS=(a "b\\ " : c); COMP_CWORD=3} -append cmd {; COMP_LINE='a b\ :c'; COMP_POINT=7} -assert_bash_exec $cmd $test -set cmd {_get_comp_words_by_ref -n : words; echo "${words[@]}"} -assert_bash_list {a "b\\ :c"} $cmd $test -sync_after_int - - -teardown diff --git a/test/unit/_get_cword.exp b/test/unit/_get_cword.exp deleted file mode 100644 index 91bcb44e..00000000 --- a/test/unit/_get_cword.exp +++ /dev/null @@ -1,37 +0,0 @@ -proc setup {} { - assert_bash_exec {unset COMP_CWORD COMP_LINE COMP_POINT COMP_WORDS} - save_env -} - - -proc teardown {} { - assert_bash_exec {unset COMP_CWORD COMP_LINE COMP_POINT COMP_WORDS} - # Delete 'COMP_WORDBREAKS' occupying two lines - assert_env_unmodified { - /COMP_WORDBREAKS=/{N - d - } - /_scp_path_esc=/d - } -} - - -setup - - -# See also ./lib/completions/alias.exp. Here `_get_cword' is actually tested -# by moving the cursor left into the current word. - -set test "index shouldn't drop below 0" -set dir $::srcdir/fixtures/_get_cword -set cmd "scp" -send "$cmd ääää§ se\t\r\n" -expect { - -re "index: substring expression < 0" { fail "$test" } - -re /@ { pass "$test" } - default { unresolved "$test" } -} -sync_after_int - - -teardown diff --git a/test/unit/_known_hosts_real.exp b/test/unit/_known_hosts_real.exp deleted file mode 100644 index 01b09b6b..00000000 --- a/test/unit/_known_hosts_real.exp +++ /dev/null @@ -1,171 +0,0 @@ -proc setup {} { - # NOTE: Changing dir to $SRCDIR is necessary because file locations in the - # ssh config files (e.g. UserKnownHostsFile) are relative to $SRCDIR. - assert_bash_exec {cd $SRCDIR} - save_env -} - - -proc teardown {} { - assert_env_unmodified { - /COMPREPLY=/d - /OLDHOME=/d - } - assert_bash_exec {cd $TESTDIR} -} - - -setup - - -set test "Hosts should be put in COMPREPLY" -set hosts [get_hosts -unsorted] -# Hosts `gee', `hus' and `jar' are defined in -# ./fixtures/_known_hosts_real/config -# doo, ike, jub, 10.0.0.1, kyl, 100.0.0.2, 10.10.0.3, blah, and bunch of IPv6 -# test cases in ./fixtures/_known_hosts_real/known_hosts -lappend hosts blah doo gee hus ike jar jub kyl 10.0.0.1 100.0.0.2 10.10.0.3 fd00:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:5555 fe80::123:0xff:dead:beef%eth0 1111:2222:3333:4444:5555:6666:xxxx:abab 11xx:2222:3333:4444:5555:6666:xxxx:abab ::42 -set cmd {unset COMPREPLY; _known_hosts_real -aF fixtures/_known_hosts_real/config ''; echo_array COMPREPLY} -assert_bash_list $hosts $cmd $test -sort -sync_after_int - -set test "Hosts should have username prefix and colon suffix" -set hosts [get_hosts -unsorted] -# Hosts `gee', `hus' and `jar' are defined in -# ./fixtures/_known_hosts_real/config -# doo, ike, jub, 10.0.0.1, kyl, 100.0.0.2, 10.10.0.3, blah, and bunch of IPv6 -# test cases in ./fixtures/_known_hosts_real/known_hosts -lappend hosts blah doo gee hus ike jar jub kyl 10.0.0.1 100.0.0.2 10.10.0.3 fd00:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:5555 fe80::123:0xff:dead:beef%eth0 1111:2222:3333:4444:5555:6666:xxxx:abab 11xx:2222:3333:4444:5555:6666:xxxx:abab ::42 -set hosts [lsort -ascii $hosts] -set expected {} -foreach host $hosts { - lappend expected "user@$host:" -} -# Call _known_hosts -set cmd {unset COMPREPLY; _known_hosts_real -acF fixtures/_known_hosts_real/config 'user@'; echo_array COMPREPLY} -assert_bash_list $expected $cmd $test -sort -sync_after_int - -set test "Files containing consecutive spaces should work" -set hosts [get_hosts -unsorted] -set hosts_orig $hosts -# Hosts `gee' and `hus' are defined in -#`./fixtures/_known_hosts_real/spaced conf' -# Hosts `two*' are defined in ./fixtures/_known_hosts_real/known_hosts2 -lappend hosts gee hus two two2 two3 two4 -set hosts_config $hosts -# Hosts `doo' and `ike' are defined in -# `./fixtures/_known_hosts_/spaced known_hosts' -lappend hosts doo ike -set hosts [join [bash_sort $hosts ] "\\s+"] -set hosts_orig [join [bash_sort $hosts_orig ] "\\s+"] -set hosts_config [join [bash_sort $hosts_config] "\\s+"] -# Call _known_hosts -set cmd {unset COMPREPLY; _known_hosts_real -aF 'fixtures/_known_hosts_real/spaced conf' ''; echo_array COMPREPLY} -send "$cmd\r" -expect -ex "$cmd\r\n" -expect { - -re "^$hosts\r\n/@$" { pass "$test" } - -re "^$hosts_orig\r\n/@$" { fail "$test (config file)" } - -re "^$hosts_config\r\n/@$" { fail "$test (known hosts file)" } - -re /@ { unresolved "$test at prompt" } - default { unresolved "$test" } -} -sync_after_int - -set test "Files starting with tilde (~) should work" -set hosts [get_hosts -unsorted] -# Hosts `two*' are defined in ./fixtures/_known_hosts_real/known_hosts2 -# Host `three' is defined in ./fixtures/_known_hosts_real/known_hosts3 -# Host `four' is defined in ./fixtures/_known_hosts_real/known_hosts4 -lappend hosts two two2 two3 two4 three four -set hosts [join [bash_sort $hosts] "\\s+"] -# Setup environment -set cmd {OLDHOME=$HOME; HOME=$SRCDIRABS} -send "$cmd\r" -expect -ex "$cmd\r\n/@" -# Call _known_hosts -set cmd {unset COMPREPLY; _known_hosts_real -aF fixtures/_known_hosts_real/config_tilde ''; echo_array COMPREPLY} -send "$cmd\r" -expect -ex "$cmd\r\n" -expect { - -re "^$hosts\r\n/@$" { pass "$test" } - default { unresolved "$test" } -} -# Teardown environment -set cmd {HOME=$OLDHOME} -send "$cmd\r" -expect -ex "$cmd\r\n/@" -sync_after_int - -set test "Empty COMP_KNOWN_HOSTS_WITH_HOSTFILE should omit HOSTFILE" -assert_bash_exec "COMP_KNOWN_HOSTS_WITH_HOSTFILE=" -set hosts [get_hosts_avahi] -# Hosts `gee', `hus' and `jar' are defined in -# ./fixtures/_known_hosts_real/config -# doo, ike, jub, 10.0.0.1, kyl, 100.0.0.2, 10.10.0.3, blah, and bunch of IPv6 -# test cases in ./fixtures/_known_hosts_real/known_hosts -lappend hosts blah doo gee hus ike jar jub kyl 10.0.0.1 100.0.0.2 10.10.0.3 fd00:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:5555 fe80::123:0xff:dead:beef%eth0 1111:2222:3333:4444:5555:6666:xxxx:abab 11xx:2222:3333:4444:5555:6666:xxxx:abab ::42 -# Call _known_hosts -set cmd {unset COMPREPLY; _known_hosts_real -aF fixtures/_known_hosts_real/config ''; echo_array COMPREPLY} -assert_bash_list $hosts $cmd $test -sort -sync_after_int -assert_bash_exec "unset -v COMP_KNOWN_HOSTS_WITH_HOSTFILE" -sync_after_int - -set test "Included config files should work" -set hosts [get_hosts -unsorted] -# Host 'recursion' is defined in -# ./fixtures/_known_hosts_real/config_include_recursion -# Host 'relative_path' is defined in -# ./fixtures/_known_hosts_real/.ssh/config_relative_path -lappend hosts recursion relative_path -set hosts [join [bash_sort $hosts] "\\s+"] -# Setup environment -# Redefined HOME to handle relative path inclusions on $HOME/.ssh -set cmd {OLDHOME=$HOME; HOME="$SRCDIRABS/fixtures/_known_hosts_real"} -send "$cmd\r" -expect -ex "$cmd\r\n/@" -# Call _known_hosts -set cmd {unset COMPREPLY; _known_hosts_real -aF fixtures/_known_hosts_real/config_include ''; echo_array COMPREPLY} -send "$cmd\r" -expect -ex "$cmd\r\n" -expect { - -re "^$hosts\r\n/@$" { pass "$test" } - default { unresolved "$test" } -} -# Teardown environment -set cmd {HOME=$OLDHOME} -send "$cmd\r" -expect -ex "$cmd\r\n/@" -sync_after_int - -set test "IPv6 filtering should work" -set hosts "127.0.0.1 localhost" -assert_bash_exec "COMP_KNOWN_HOSTS_WITH_HOSTFILE=" -set cmd {unset COMPREPLY; _known_hosts_real -4F fixtures/_known_hosts_real/localhost_config ''; echo_array COMPREPLY} -assert_bash_list $hosts $cmd $test -sort -sync_after_int -assert_bash_exec "unset -v COMP_KNOWN_HOSTS_WITH_HOSTFILE" -sync_after_int - -set test "IPv4 filtering should work" -set hosts "::1 localhost" -assert_bash_exec "COMP_KNOWN_HOSTS_WITH_HOSTFILE=" -set cmd {unset COMPREPLY; _known_hosts_real -6F fixtures/_known_hosts_real/localhost_config ''; echo_array COMPREPLY} -assert_bash_list $hosts $cmd $test -sort -sync_after_int -assert_bash_exec "unset -v COMP_KNOWN_HOSTS_WITH_HOSTFILE" -sync_after_int - -set test "IPv4+IPv6 filtering should work" -set hosts "localhost" -assert_bash_exec "COMP_KNOWN_HOSTS_WITH_HOSTFILE=" -set cmd {unset COMPREPLY; _known_hosts_real -46F fixtures/_known_hosts_real/localhost_config ''; echo_array COMPREPLY} -assert_bash_list $hosts $cmd $test -sort -sync_after_int -assert_bash_exec "unset -v COMP_KNOWN_HOSTS_WITH_HOSTFILE" -sync_after_int - - -teardown diff --git a/test/update-test-cmd-list b/test/update-test-cmd-list new file mode 100755 index 00000000..115ae165 --- /dev/null +++ b/test/update-test-cmd-list @@ -0,0 +1,13 @@ +#!/bin/bash -eu + +mydir=$( + cd "$(dirname "$0")" + pwd +) + +cat "$mydir"/t/test_*.py | + tr -d '\n' | + grep -Eo '@pytest.mark.complete(\([^)]*\))' | + sed -ne 's/^[^"]*"\\\?\([^_][^[:space:]"]*\)[[:space:]"].*/\1/p' | + sort -u \ + >"$mydir"/test-cmd-list.txt |