1import subprocess 2import sys 3 4import pytest 5 6# Test for _argcomplete but not specific for any application. 7 8 9def equal_with_bash(prefix, ffc, fc, out=None): 10 res = ffc(prefix) 11 res_bash = set(fc(prefix)) 12 retval = set(res) == res_bash 13 if out: 14 out.write("equal_with_bash({}) {} {}\n".format(prefix, retval, res)) 15 if not retval: 16 out.write(" python - bash: %s\n" % (set(res) - res_bash)) 17 out.write(" bash - python: %s\n" % (res_bash - set(res))) 18 return retval 19 20 21# Copied from argcomplete.completers as import from there. 22# Also pulls in argcomplete.__init__ which opens filedescriptor 9. 23# This gives an OSError at the end of testrun. 24 25 26def _wrapcall(*args, **kargs): 27 try: 28 return subprocess.check_output(*args, **kargs).decode().splitlines() 29 except subprocess.CalledProcessError: 30 return [] 31 32 33class FilesCompleter: 34 """File completer class, optionally takes a list of allowed extensions.""" 35 36 def __init__(self, allowednames=(), directories=True): 37 # Fix if someone passes in a string instead of a list 38 if type(allowednames) is str: 39 allowednames = [allowednames] 40 41 self.allowednames = [x.lstrip("*").lstrip(".") for x in allowednames] 42 self.directories = directories 43 44 def __call__(self, prefix, **kwargs): 45 completion = [] 46 if self.allowednames: 47 if self.directories: 48 files = _wrapcall( 49 ["bash", "-c", "compgen -A directory -- '{p}'".format(p=prefix)] 50 ) 51 completion += [f + "/" for f in files] 52 for x in self.allowednames: 53 completion += _wrapcall( 54 [ 55 "bash", 56 "-c", 57 "compgen -A file -X '!*.{0}' -- '{p}'".format(x, p=prefix), 58 ] 59 ) 60 else: 61 completion += _wrapcall( 62 ["bash", "-c", "compgen -A file -- '{p}'".format(p=prefix)] 63 ) 64 65 anticomp = _wrapcall( 66 ["bash", "-c", "compgen -A directory -- '{p}'".format(p=prefix)] 67 ) 68 69 completion = list(set(completion) - set(anticomp)) 70 71 if self.directories: 72 completion += [f + "/" for f in anticomp] 73 return completion 74 75 76class TestArgComplete: 77 @pytest.mark.skipif("sys.platform in ('win32', 'darwin')") 78 def test_compare_with_compgen(self, tmpdir): 79 from _pytest._argcomplete import FastFilesCompleter 80 81 ffc = FastFilesCompleter() 82 fc = FilesCompleter() 83 84 with tmpdir.as_cwd(): 85 assert equal_with_bash("", ffc, fc, out=sys.stdout) 86 87 tmpdir.ensure("data") 88 89 for x in ["d", "data", "doesnotexist", ""]: 90 assert equal_with_bash(x, ffc, fc, out=sys.stdout) 91 92 @pytest.mark.skipif("sys.platform in ('win32', 'darwin')") 93 def test_remove_dir_prefix(self): 94 """This is not compatible with compgen but it is with bash itself: ls /usr/<TAB>.""" 95 from _pytest._argcomplete import FastFilesCompleter 96 97 ffc = FastFilesCompleter() 98 fc = FilesCompleter() 99 for x in "/usr/".split(): 100 assert not equal_with_bash(x, ffc, fc, out=sys.stdout) 101