Add tests for bash completion; fix Unicode bug that turned up

Note that argcomplete is also now required.
This commit is contained in:
Jim Paris 2019-08-30 00:14:23 -04:00
parent ea051c85b3
commit 9126980ed4
4 changed files with 83 additions and 12 deletions

View File

@ -12,10 +12,7 @@ import argparse
from argparse import ArgumentDefaultsHelpFormatter as def_form
import signal
try: # pragma: no cover
import argcomplete
except ImportError: # pragma: no cover
argcomplete = None
import argcomplete
# Valid subcommands. Defined in separate files just to break
# things up -- they're still called with Cmdline as self.
@ -40,7 +37,7 @@ class JimArgumentParser(argparse.ArgumentParser):
self.print_usage(sys.stderr)
self.exit(2, sprintf("error: %s\n", message))
class Complete(object): # pragma: no cover
class Complete(object):
# Completion helpers, for using argcomplete (see
# extras/nilmtool-bash-completion.sh)
def escape(self, s):
@ -80,10 +77,6 @@ class Complete(object): # pragma: no cover
if not path:
return []
results = []
# prefix comes in as UTF-8, but results need to be Unicode,
# weird. Still doesn't work in all cases, but that's bugs in
# argcomplete.
prefix = nilmdb.utils.str.decode(prefix)
for (k,v) in client.stream_get_metadata(path).items():
kv = self.escape(k + '=' + v)
if kv.startswith(prefix):
@ -98,6 +91,7 @@ class Cmdline(object):
self.def_url = os.environ.get("NILMDB_URL", "http://localhost/nilmdb/")
self.subcmd = {}
self.complete = Complete()
self.complete_output_stream = None # overridden by test suite
def arg_time(self, toparse):
"""Parse a time string argument"""
@ -153,8 +147,8 @@ class Cmdline(object):
# Run parser
self.parser_setup()
if argcomplete: # pragma: no cover
argcomplete.autocomplete(self.parser)
argcomplete.autocomplete(self.parser, exit_method=sys.exit,
output_stream=self.complete_output_stream)
self.args = self.parser.parse_args(self.argv)
# Run arg verify handler if there is one

View File

@ -1,3 +1,4 @@
argcomplete>=1.10.0
CherryPy>=18.1.2
coverage>=4.5.4
cython>=0.29.13

View File

@ -6,7 +6,6 @@
# Then just package it up:
# python setup.py sdist
import traceback
import sys
import os
from setuptools import setup

View File

@ -360,6 +360,12 @@ class TestCmdline(object):
self.ok("metadata /newton/raw --update "
"v_scale=1.234")
# unicode
self.ok("metadata /newton/raw --set "
"a_𝓴𝓮𝔂=value a_key=𝓿𝓪𝓵𝓾𝓮 a_𝗸𝗲𝘆=𝘃𝗮𝗹𝘂𝗲")
self.ok("metadata /newton/raw --get")
self.match("a_key=𝓿𝓪𝓵𝓾𝓮\na_𝓴𝓮𝔂=value\na_𝗸𝗲𝘆=𝘃𝗮𝗹𝘂𝗲\n")
# various parsing tests
self.ok("metadata /newton/raw --update foo=")
self.fail("metadata /newton/raw --update =bar")
@ -1166,3 +1172,74 @@ class TestCmdline(object):
server_stop()
server_start()
def test_05b_completion(self):
# Test bash completion. This depends on some data put in the DB by
# earlier tests, so the execution order is important.
def complete(line, expect="<unspecified>"):
# set env vars
env = {
'_ARGCOMPLETE': '1',
'COMP_LINE': line,
'COMP_POINT': str(len(line)),
'COMP_TYPE': '8',
'NILMDB_URL': "http://localhost:32180/",
}
for (k, v) in env.items():
os.environ[k] = v
# create pipe for completion output
output = io.BytesIO()
# ensure argcomplete won't mess with any FDs
def fake_fdopen(fd, mode):
return io.BytesIO()
old_fdopen = os.fdopen
os.fdopen = fake_fdopen
# run cli
cmdline = nilmdb.cmdline.Cmdline([])
cmdline.complete_output_stream = output
try:
cmdline.run()
sys.exit(0)
except SystemExit as e:
exitcode = e.code
eq_(exitcode, 0)
# clean up
os.fdopen = old_fdopen
for (k, v) in env.items():
del os.environ[k]
# read completion output
comp = output.getvalue()
# replace completion separators with commas, for clarity
cleaned = comp.replace(b'\x0b', b',').decode('utf-8')
# expect the given match or prefix
if expect.endswith('*'):
if not cleaned.startswith(expect[:-1]):
raise AssertionError(("completions:\n '%s'\n"
"don't start with:\n '%s'") %
(cleaned, expect[:-1]))
else:
if cleaned != expect:
raise AssertionError(("completions:\n '%s'\n"
"don't match:\n '%s'") %
(cleaned, expect))
complete("nilmtool -u ", "")
complete("nilmtool list ", "-h,--help,-E,--ext*")
complete("nilmtool list --st", "--start ")
complete("nilmtool list --start ", "")
complete("nilmtool list /", "/newton/prep,/newton/raw*")
complete("nilmtool create /foo int3", "int32_1,int32_2*")
complete("nilmtool metadata /newton/raw --get a",
"a_𝓴𝓮𝔂,a_key,a_𝗸𝗲𝘆")
complete("nilmtool metadata /newton/raw --set a",
"a_𝓴𝓮𝔂=value,a_key=𝓿𝓪𝓵𝓾𝓮,a_𝗸𝗲𝘆=𝘃𝗮𝗹𝘂𝗲")
complete("nilmtool metadata /newton/raw --set a_𝗸", "a_𝗸𝗲𝘆=𝘃𝗮𝗹𝘂𝗲 ")
complete("nilmtool metadata '' --set a", "")
self.run("list")