Tests and improvements for KeyConfig

This commit is contained in:
Florian Bruhin 2017-07-02 22:10:28 +02:00
parent 725ffef5f3
commit 31b999ea59
2 changed files with 131 additions and 31 deletions

View File

@ -131,6 +131,15 @@ class KeyConfig:
def __init__(self, config):
self._config = config
def _prepare(self, key, mode):
"""Make sure the given mode exists and normalize the key."""
if mode not in configdata.DATA['bindings.default'].default:
raise configexc.KeybindingError("Invalid mode {}!".format(mode))
if utils.is_special_key(key):
# <Ctrl-t>, <ctrl-T>, and <ctrl-t> should be considered equivalent
return utils.normalize_keystr(key)
return key
def get_bindings_for(self, mode):
"""Get the combined bindings for the given mode."""
bindings = dict(val.bindings.default[mode])
@ -156,14 +165,11 @@ class KeyConfig:
cmd_to_keys[cmd].insert(0, key)
return cmd_to_keys
def _prepare(self, key, mode):
"""Make sure the given mode exists and normalize the key."""
if mode not in configdata.DATA['bindings.default'].default:
raise configexc.KeybindingError("Invalid mode {}!".format(mode))
if utils.is_special_key(key):
# <Ctrl-t>, <ctrl-T>, and <ctrl-t> should be considered equivalent
return utils.normalize_keystr(key)
return key
def get_command(self, key, mode):
"""Get the command for a given key (or None)."""
key = self._prepare(key, mode)
bindings = self.get_bindings_for(mode)
return bindings.get(key, None)
def bind(self, key, command, *, mode, force=False, save_yaml=False):
"""Add a new binding from key to command."""
@ -175,7 +181,7 @@ class KeyConfig:
except cmdexc.Error as e:
raise configexc.KeybindingError("Invalid command: {}".format(e))
for result in results:
for result in results: # pragma: no branch
try:
result.cmd.validate_mode(usertypes.KeyMode[mode])
except cmdexc.PrerequisitesError as e:
@ -207,16 +213,11 @@ class KeyConfig:
bindings_commands[mode] = {}
bindings_commands[mode][key] = None
else:
raise configexc.KeybindingError("Can't find binding '{}' in section '{}'!"
.format(key, mode))
raise configexc.KeybindingError(
"Can't find binding '{}' in {} mode".format(key, mode))
self._config.update_mutables(save_yaml=save_yaml)
def get_command(self, key, mode):
"""Get the command for a given key (or None)."""
key = self._prepare(key, mode)
return val.bindings.commands[mode].get(key, None)
class ConfigCommands:

View File

@ -89,6 +89,24 @@ class TestKeyConfig:
config_stub.val.aliases = {}
return config.KeyConfig(config_stub)
@pytest.fixture
def no_bindings(self):
"""Get a dict with no bindings."""
return {'normal': {}}
@pytest.mark.parametrize('key, expected', [
('A', 'A'),
('<Ctrl-X>', '<ctrl+x>'),
])
def test_prepare_valid(self, keyconf, key, expected):
"""Make sure prepare normalizes the key."""
assert keyconf._prepare(key, 'normal') == expected
def test_prepare_invalid(self, keyconf):
"""Make sure prepare checks the mode."""
with pytest.raises(configexc.KeybindingError):
assert keyconf._prepare('x', 'abnormal')
@pytest.mark.parametrize('commands, expected', [
# Unbinding default key
({'a': None}, {'b': 'message-info bar'}),
@ -98,7 +116,8 @@ class TestKeyConfig:
# Unbinding unknown key
({'x': None}, {'a': 'message-info foo', 'b': 'message-info bar'}),
])
def test_get_bindings_for(self, keyconf, config_stub, commands, expected):
def test_get_bindings_for_and_get_command(self, keyconf, config_stub,
commands, expected):
orig_default_bindings = {'normal': {'a': 'message-info foo',
'b': 'message-info bar'},
'insert': {},
@ -115,6 +134,13 @@ class TestKeyConfig:
# Make sure the code creates a copy and doesn't modify the setting
assert config_stub.val.bindings.default == orig_default_bindings
assert bindings == expected
for key, command in expected.items():
assert keyconf.get_command(key, 'normal') == command
def test_get_command_unbound(self, keyconf, config_stub, no_bindings):
config_stub.val.bindings.default = no_bindings
config_stub.val.bindings.commands = no_bindings
assert keyconf.get_command('foobar', 'normal') is None
@pytest.mark.parametrize('bindings, expected', [
# Simple
@ -130,24 +156,97 @@ class TestKeyConfig:
({'a': 'message-info foo ;; message-info bar'},
{'message-info foo': ['a'], 'message-info bar': ['a']}),
])
def test_get_reverse_bindings_for(self, keyconf, config_stub, bindings,
expected):
config_stub.val.bindings.default = {'normal': {}}
def test_get_reverse_bindings_for(self, keyconf, config_stub, no_bindings,
bindings, expected):
config_stub.val.bindings.default = no_bindings
config_stub.val.bindings.commands = {'normal': bindings}
assert keyconf.get_reverse_bindings_for('normal') == expected
@pytest.mark.parametrize('key, expected', [
('A', 'A'),
('<Ctrl-X>', '<ctrl+x>'),
])
def test_prepare_valid(self, keyconf, key, expected):
"""Make sure prepare normalizes the key."""
assert keyconf._prepare(key, 'normal') == expected
def test_bind_invalid_command(self, keyconf):
with pytest.raises(configexc.KeybindingError,
match='Invalid command: foobar'):
keyconf.bind('a', 'foobar', mode='normal')
def test_prepare_invalid(self, keyconf):
"""Make sure prepare checks the mode."""
with pytest.raises(configexc.KeybindingError):
assert keyconf._prepare('x', 'abnormal')
def test_bind_invalid_mode(self, keyconf):
with pytest.raises(configexc.KeybindingError,
match='completion-item-del: This command is only '
'allowed in command mode, not normal.'):
keyconf.bind('a', 'completion-item-del', mode='normal')
@pytest.mark.parametrize('as_default', [True, False])
@pytest.mark.parametrize('force', [True, False])
@pytest.mark.parametrize('key', ['a', '<Ctrl-X>'])
def test_bind_duplicate(self, keyconf, config_stub, no_bindings,
as_default, force, key):
bindings = {'normal': {'a': 'nop', '<Ctrl+x>': 'nop'}}
if as_default:
config_stub.val.bindings.default = bindings
config_stub.val.bindings.commands = no_bindings
else:
config_stub.val.bindings.default = no_bindings
config_stub.val.bindings.commands = bindings
if force:
keyconf.bind(key, 'message-info foo', mode='normal', force=True)
assert keyconf.get_command(key, 'normal') == 'message-info foo'
else:
with pytest.raises(configexc.DuplicateKeyError):
keyconf.bind(key, 'message-info foo', mode='normal')
assert keyconf.get_command(key, 'normal') == 'nop'
@pytest.mark.parametrize('mode', ['normal', 'caret'])
def test_bind(self, keyconf, config_stub, qtbot, no_bindings, mode):
config_stub.val.bindings.default = no_bindings
config_stub.val.bindings.commands = no_bindings
command = 'message-info foo'
with qtbot.wait_signal(config_stub.changed):
keyconf.bind('a', command, mode=mode)
assert config_stub.val.bindings.commands[mode]['a'] == command
assert keyconf.get_bindings_for(mode)['a'] == command
assert keyconf.get_command('a', mode) == command
@pytest.mark.parametrize('as_default', [True, False])
@pytest.mark.parametrize('key, normalized', [
('a', 'a'),
('<Ctrl-X>', '<ctrl+x>')
])
@pytest.mark.parametrize('mode', ['normal', 'caret'])
def test_unbind(self, keyconf, config_stub, qtbot, no_bindings,
as_default, key, normalized, mode):
bindings = {
'normal': {'a': 'nop', '<ctrl+x>': 'nop'},
'caret': {'a': 'nop', '<ctrl+x>': 'nop'},
}
if as_default:
config_stub.val.bindings.default = bindings
config_stub.val.bindings.commands = no_bindings
else:
config_stub.val.bindings.default = no_bindings
config_stub.val.bindings.commands = bindings
with qtbot.wait_signal(config_stub.changed):
keyconf.unbind(key, mode=mode)
assert keyconf.get_command(key, mode) is None
mode_bindings = config_stub.val.bindings.commands[mode]
if as_default:
default_bindings = config_stub.val.bindings.default
assert default_bindings[mode] == bindings[mode]
assert mode_bindings[normalized] is None
else:
assert normalized not in mode_bindings
def test_unbind_unbound(self, keyconf, config_stub, no_bindings):
"""Try unbinding a key which is not bound."""
config_stub.val.bindings.default = no_bindings
config_stub.val.bindings.commands = no_bindings
with pytest.raises(configexc.KeybindingError,
match="Can't find binding 'foobar' in normal mode"):
keyconf.unbind('foobar', mode='normal')
class StyleObj(QObject):