fix fuzzy search for same-distance case

get_fuzzy_matches() cannot handle case where all
commands have the same Levenshtein distance
from searched command. It will bomb-out with
IndexError (dist[i][1] referencing one element
after the end).

It also does not handle no-commands case or
the case where searched command is the prefix of
all commands. The latter two cases are minor, since
'help' and 'complete' commands are always present.

We fix it by iterating over the partial result
safely, consuming only candidates with distance==0
(prefix commands) and those of the same minimum
distance.

Closes-Bug: #1500445
Change-Id: Iae5e77b64dc0d96e040acdcadf019db66a416648
This commit is contained in:
Brano Zarnovican
2015-09-28 14:33:51 +02:00
parent e3a24897a7
commit b8e7a78a45
2 changed files with 53 additions and 13 deletions

View File

@@ -13,6 +13,7 @@ from cliff.app import App
from cliff.command import Command
from cliff.commandmanager import CommandManager
from cliff.tests import utils
from cliff.utils import damerau_levenshtein, COST
def make_app(**kwargs):
@@ -448,3 +449,42 @@ def test_list_matching_commands():
assert "test: 't' is not a test command. See 'test --help'." in output
assert 'Did you mean one of these?' in output
assert 'three word command\n two words\n' in output
def test_fuzzy_no_commands():
cmd_mgr = CommandManager('cliff.fuzzy')
app = App('test', '1.0', cmd_mgr)
cmd_mgr.commands = {}
matches = app.get_fuzzy_matches('foo')
assert matches == []
def test_fuzzy_common_prefix():
# searched string is a prefix of all commands
cmd_mgr = CommandManager('cliff.fuzzy')
app = App('test', '1.0', cmd_mgr)
cmd_mgr.commands = {}
cmd_mgr.add_command('user list', utils.TestCommand)
cmd_mgr.add_command('user show', utils.TestCommand)
matches = app.get_fuzzy_matches('user')
assert matches == ['user list', 'user show']
def test_fuzzy_same_distance():
# searched string has the same distance to all commands
cmd_mgr = CommandManager('cliff.fuzzy')
app = App('test', '1.0', cmd_mgr)
cmd_mgr.add_command('user', utils.TestCommand)
for cmd in cmd_mgr.commands.keys():
assert damerau_levenshtein('node', cmd, COST) == 8
matches = app.get_fuzzy_matches('node')
assert matches == ['complete', 'help', 'user']
def test_fuzzy_no_prefix():
# search by distance, no common prefix with any command
cmd_mgr = CommandManager('cliff.fuzzy')
app = App('test', '1.0', cmd_mgr)
cmd_mgr.add_command('user', utils.TestCommand)
matches = app.get_fuzzy_matches('uesr')
assert matches == ['user']