summaryrefslogtreecommitdiff
path: root/test/t
diff options
context:
space:
mode:
authorGabriel F. T. Gomes <gabriel@inconstante.net.br>2020-08-03 18:43:13 -0300
committerGabriel F. T. Gomes <gabriel@inconstante.net.br>2020-08-03 18:43:13 -0300
commit95623d39d6029ba78ec96ad5ea08e9ac12629b91 (patch)
treeea0fe36eb5e6f40e0a1f765d44c4b0c0b2bfb089 /test/t
parent019f3cc463db63abc6460f97deb488deec43840b (diff)
downloadbash-completion-95623d39d6029ba78ec96ad5ea08e9ac12629b91.tar.gz
New upstream version 2.11upstream/2.11upstream
Diffstat (limited to 'test/t')
-rw-r--r--test/t/Makefile.am9
-rw-r--r--test/t/conftest.py367
-rw-r--r--test/t/test_2to3.py2
-rw-r--r--test/t/test_7z.py5
-rw-r--r--test/t/test_alias.py4
-rw-r--r--test/t/test_ant.py13
-rw-r--r--test/t/test_apt_cache.py6
-rw-r--r--test/t/test_apt_get.py6
-rw-r--r--test/t/test_aptitude.py16
-rw-r--r--test/t/test_arpspoof.py7
-rw-r--r--test/t/test_bmake.py7
-rw-r--r--test/t/test_ccache.py6
-rw-r--r--test/t/test_cd.py6
-rw-r--r--test/t/test_chown.py34
-rw-r--r--test/t/test_complete.py4
-rw-r--r--test/t/test_cpan2dist.py4
-rw-r--r--test/t/test_cpio.py12
-rw-r--r--test/t/test_cppcheck.py6
-rw-r--r--test/t/test_crontab.py9
-rw-r--r--test/t/test_curl.py8
-rw-r--r--test/t/test_cvs.py2
-rw-r--r--test/t/test_dd.py2
-rw-r--r--test/t/test_dmypy.py6
-rw-r--r--test/t/test_dnssec_keygen.py2
-rw-r--r--test/t/test_dpkg_deb.py4
-rw-r--r--test/t/test_dpkg_query.py18
-rw-r--r--test/t/test_feh.py4
-rw-r--r--test/t/test_find.py13
-rw-r--r--test/t/test_finger.py24
-rw-r--r--test/t/test_gcc.py11
-rw-r--r--test/t/test_ip.py4
-rw-r--r--test/t/test_ipcalc.py23
-rw-r--r--test/t/test_irb.py2
-rw-r--r--test/t/test_iscsiadm.py4
-rw-r--r--test/t/test_isort.py2
-rw-r--r--test/t/test_jsonschema.py4
-rw-r--r--test/t/test_kcov.py2
-rw-r--r--test/t/test_ldd.py4
-rw-r--r--test/t/test_less.py4
-rw-r--r--test/t/test_lftp.py14
-rw-r--r--test/t/test_lilo.py9
-rw-r--r--test/t/test_ls.py2
-rw-r--r--test/t/test_lspci.py4
-rw-r--r--test/t/test_make.py19
-rw-r--r--test/t/test_man.py26
-rw-r--r--test/t/test_mkdir.py3
-rw-r--r--test/t/test_modprobe.py2
-rw-r--r--test/t/test_mount.py2
-rw-r--r--test/t/test_mr.py13
-rw-r--r--test/t/test_mypy.py2
-rw-r--r--test/t/test_nmap.py44
-rw-r--r--test/t/test_openssl.py5
-rw-r--r--test/t/test_perl.py2
-rw-r--r--test/t/test_pgrep.py22
-rw-r--r--test/t/test_postfix.py16
-rw-r--r--test/t/test_printenv.py19
-rw-r--r--test/t/test_protoc.py9
-rw-r--r--test/t/test_pydocstyle.py4
-rw-r--r--test/t/test_pylint.py2
-rw-r--r--test/t/test_pytest.py39
-rw-r--r--test/t/test_python.py4
-rw-r--r--test/t/test_python3.py4
-rw-r--r--test/t/test_ri.py2
-rw-r--r--test/t/test_sbcl.py3
-rw-r--r--test/t/test_sbcl_mt.py3
-rw-r--r--test/t/test_scp.py79
-rw-r--r--test/t/test_screen.py8
-rw-r--r--test/t/test_secret_tool.py12
-rw-r--r--test/t/test_sftp.py37
-rw-r--r--test/t/test_slapt_get.py28
-rw-r--r--test/t/test_slapt_src.py31
-rw-r--r--test/t/test_ssh.py28
-rw-r--r--test/t/test_ssh_keygen.py51
-rw-r--r--test/t/test_sudo.py36
-rw-r--r--test/t/test_tar.py20
-rw-r--r--test/t/test_totem.py7
-rw-r--r--test/t/test_tshark.py9
-rw-r--r--test/t/test_tsig_keygen.py12
-rw-r--r--test/t/test_umount.py78
-rw-r--r--test/t/test_upgradepkg.py17
-rw-r--r--test/t/test_userdel.py4
-rw-r--r--test/t/test_valgrind.py4
-rw-r--r--test/t/test_wol.py7
-rw-r--r--test/t/test_write.py4
-rw-r--r--test/t/test_xfreerdp.py32
-rw-r--r--test/t/test_xgamma.py2
-rw-r--r--test/t/test_xhost.py22
-rw-r--r--test/t/unit/Makefile.am5
-rw-r--r--test/t/unit/test_unit_count_args.py4
-rw-r--r--test/t/unit/test_unit_expand.py20
-rw-r--r--test/t/unit/test_unit_expand_tilde_by_ref.py32
-rw-r--r--test/t/unit/test_unit_filedir.py138
-rw-r--r--test/t/unit/test_unit_get_comp_words_by_ref.py101
-rw-r--r--test/t/unit/test_unit_get_cword.py25
-rw-r--r--test/t/unit/test_unit_init_completion.py16
-rw-r--r--test/t/unit/test_unit_known_hosts_real.py158
-rw-r--r--test/t/unit/test_unit_longopt.py18
-rw-r--r--test/t/unit/test_unit_quote.py2
-rw-r--r--test/t/unit/test_unit_quote_readline.py15
-rw-r--r--test/t/unit/test_unit_variables.py10
-rw-r--r--test/t/unit/test_unit_xinetd_services.py22
101 files changed, 1675 insertions, 359 deletions
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"]