We could potentially end up recursing over the entire file system. And for completion we only present the candidates in the immediate directory anyway.
110 lines
3.3 KiB
Python
110 lines
3.3 KiB
Python
#!/usr/bin/env python
|
|
# License: GPLv3 Copyright: 2022, Kovid Goyal <kovid at kovidgoyal.net>
|
|
|
|
|
|
import json
|
|
import os
|
|
import shlex
|
|
import subprocess
|
|
import tempfile
|
|
|
|
from kitty.constants import kitty_tool_exe as kitty_tool
|
|
|
|
from . import BaseTest
|
|
|
|
|
|
class TestCompletion(BaseTest):
|
|
|
|
def test_completion(self):
|
|
with tempfile.TemporaryDirectory() as tdir:
|
|
completion(self, tdir)
|
|
|
|
|
|
def has_words(*words):
|
|
def t(self, result):
|
|
q = set(words)
|
|
for group in result.get('groups', ()):
|
|
for m in group['matches']:
|
|
if m['word'] in words:
|
|
q.discard(m['word'])
|
|
self.assertFalse(q, f'Command line: {self.current_cmd!r}')
|
|
return t
|
|
|
|
|
|
def all_words(*words):
|
|
def t(self, result):
|
|
expected = set(words)
|
|
actual = set()
|
|
|
|
for group in result.get('groups', ()):
|
|
for m in group['matches']:
|
|
actual.add(m['word'])
|
|
self.assertEqual(expected, actual, f'Command line: {self.current_cmd!r}')
|
|
return t
|
|
|
|
|
|
def completion(self: TestCompletion, tdir: str):
|
|
all_cmds = []
|
|
all_argv = []
|
|
all_tests = []
|
|
|
|
def add(cmdline: str, *tests):
|
|
all_cmds.append(cmdline)
|
|
new_word = cmdline.endswith(' ')
|
|
if new_word:
|
|
cmdline = cmdline[:-1]
|
|
all_argv.append(shlex.split(cmdline))
|
|
if new_word:
|
|
all_argv[-1].append('')
|
|
all_tests.append(tests)
|
|
|
|
def run_tool():
|
|
env = os.environ.copy()
|
|
env['PATH'] = os.path.join(tdir, 'bin')
|
|
env['HOME'] = os.path.join(tdir, 'sub')
|
|
cp = subprocess.run(
|
|
[kitty_tool(), '__complete__', 'json'],
|
|
check=True, stdout=subprocess.PIPE, cwd=tdir, input=json.dumps(all_argv).encode(), env=env
|
|
)
|
|
self.assertEqual(cp.returncode, 0, f'kitty-tool __complete__ failed with exit code: {cp.returncode}')
|
|
return json.loads(cp.stdout)
|
|
|
|
add('kitty ', has_words('@', '@ls'))
|
|
add('kitty @ l', has_words('ls', 'last-used-layout', 'launch'))
|
|
add('kitty @l', has_words('@ls', '@last-used-layout', '@launch'))
|
|
|
|
def make_file(path, mode=None):
|
|
with open(os.path.join(tdir, path), mode='x') as f:
|
|
if mode is not None:
|
|
os.chmod(f.fileno(), mode)
|
|
|
|
os.mkdir(os.path.join(tdir, 'bin'))
|
|
os.mkdir(os.path.join(tdir, 'sub'))
|
|
make_file('bin/exe1', 0o700)
|
|
make_file('bin/exe-not1')
|
|
make_file('exe2', 0o700)
|
|
make_file('exe-not2.jpeg')
|
|
make_file('sub/exe3', 0o700)
|
|
make_file('sub/exe-not3.png')
|
|
|
|
add('kitty x', all_words())
|
|
add('kitty e', all_words('exe1'))
|
|
add('kitty ./', all_words('./bin/', './sub/', './exe2'))
|
|
add('kitty ./e', all_words('./exe2'))
|
|
add('kitty ./s', all_words('./sub/'))
|
|
add('kitty ~', all_words('~/exe3'))
|
|
add('kitty ~/', all_words('~/exe3'))
|
|
add('kitty ~/e', all_words('~/exe3'))
|
|
|
|
add('kitty @ goto-layout ', has_words('tall', 'fat'))
|
|
add('kitty @ goto-layout spli', all_words('splits'))
|
|
add('kitty @ goto-layout f f', all_words())
|
|
add('kitty @ set-window-logo ', all_words('exe-not2.jpeg', 'sub/', 'bin/'))
|
|
add('kitty @ set-window-logo e', all_words('exe-not2.jpeg'))
|
|
add('kitty @ set-window-logo e e', all_words())
|
|
|
|
for cmd, tests, result in zip(all_cmds, all_tests, run_tool()):
|
|
self.current_cmd = cmd
|
|
for test in tests:
|
|
test(self, result)
|