From 0286e9ddf28b9b8d8bcbce188865d3621bc2fd5c Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Sat, 12 Aug 2017 15:07:17 -0400 Subject: [PATCH] Fix completion tests after config merge. --- qutebrowser/completion/models/configmodel.py | 25 +- qutebrowser/completion/models/miscmodels.py | 46 ++-- qutebrowser/config/config.py | 4 +- tests/end2end/features/completion.feature | 4 +- tests/helpers/stubs.py | 10 - tests/unit/completion/test_completer.py | 16 +- tests/unit/completion/test_histcategory.py | 6 +- tests/unit/completion/test_models.py | 226 ++++++++----------- 8 files changed, 139 insertions(+), 198 deletions(-) diff --git a/qutebrowser/completion/models/configmodel.py b/qutebrowser/completion/models/configmodel.py index 3ea7e18ab..b90b1e886 100644 --- a/qutebrowser/completion/models/configmodel.py +++ b/qutebrowser/completion/models/configmodel.py @@ -27,8 +27,9 @@ from qutebrowser.commands import cmdutils def option(): """A CompletionModel filled with settings and their descriptions.""" - model = completionmodel.CompletionModel(column_widths=(30, 70, 0)) - options = [(x.name, x.description) for x in configdata.DATA.values()] + model = completionmodel.CompletionModel(column_widths=(20, 70, 10)) + options = ((x.name, x.description, config.instance.get_str(x.name)) + for x in configdata.DATA.values()) model.add_category(listcategory.ListCategory("Options", options)) return model @@ -57,23 +58,3 @@ def value(optname, *values): if vals is not None: model.add_category(listcategory.ListCategory("Completions", vals)) return model - - -def bind(key): - """A CompletionModel filled with all bindable commands and descriptions. - - Args: - key: the key being bound. - """ - model = completionmodel.CompletionModel(column_widths=(20, 60, 20)) - cmd_text = objreg.get('key-config').get_bindings_for('normal').get(key) - - if cmd_text: - cmd_name = cmd_text.split(' ')[0] - cmd = cmdutils.cmd_dict.get(cmd_name) - data = [(cmd_text, cmd.desc, key)] - model.add_category(listcategory.ListCategory("Current", data)) - - #cmdlist = _get_cmd_completions(include_hidden=True, include_aliases=True) - #model.add_category(listcategory.ListCategory("Commands", cmdlist)) - return model diff --git a/qutebrowser/completion/models/miscmodels.py b/qutebrowser/completion/models/miscmodels.py index c8b1cedaf..c0726730a 100644 --- a/qutebrowser/completion/models/miscmodels.py +++ b/qutebrowser/completion/models/miscmodels.py @@ -39,22 +39,11 @@ def helptopic(): cmdlist = _get_cmd_completions(include_aliases=False, include_hidden=True, prefix=':') - settings = [] - for sectname, sectdata in configdata.DATA.items(): - for optname in sectdata: - try: - desc = sectdata.descriptions[optname] - except (KeyError, AttributeError): - # Some stuff (especially ValueList items) don't have a - # description. - desc = "" - else: - desc = desc.splitlines()[0] - name = '{}->{}'.format(sectname, optname) - settings.append((name, desc)) + settings = ((opt.name, opt.description) + for opt in configdata.DATA.values()) model.add_category(listcategory.ListCategory("Commands", cmdlist)) - model.add_category(listcategory.ListCategory("Settings", settings)) + model.add_category(listcategory.ListCategory("Settings", sorted(settings))) return model @@ -135,6 +124,26 @@ def buffer(): return model +def bind(key): + """A CompletionModel filled with all bindable commands and descriptions. + + Args: + key: the key being bound. + """ + model = completionmodel.CompletionModel(column_widths=(20, 60, 20)) + cmd_text = config.key_instance.get_bindings_for('normal').get(key) + + if cmd_text: + cmd_name = cmd_text.split(' ')[0] + cmd = cmdutils.cmd_dict.get(cmd_name) + data = [(cmd_text, cmd.desc, key)] + model.add_category(listcategory.ListCategory("Current", data)) + + cmdlist = _get_cmd_completions(include_hidden=True, include_aliases=True) + model.add_category(listcategory.ListCategory("Commands", cmdlist)) + return model + + def _get_cmd_completions(include_hidden, include_aliases, prefix=''): """Get a list of completions info for commands, sorted by name. @@ -155,10 +164,9 @@ def _get_cmd_completions(include_hidden, include_aliases, prefix=''): bindings = ', '.join(cmd_to_keys.get(obj.name, [])) cmdlist.append((prefix + obj.name, obj.desc, bindings)) - # FIXME:conf - # if include_aliases: - # for name, cmd in config.section('aliases').items(): - # bindings = ', '.join(cmd_to_keys.get(name, [])) - # cmdlist.append((name, "Alias for '{}'".format(cmd), bindings)) + if include_aliases: + for name, cmd in config.val.aliases.items(): + bindings = ', '.join(cmd_to_keys.get(name, [])) + cmdlist.append((name, "Alias for '{}'".format(cmd), bindings)) return cmdlist diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index 155f3e0de..40db84ba0 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -29,7 +29,7 @@ from qutebrowser.config import configdata, configexc, configtypes, configfiles from qutebrowser.utils import utils, objreg, message, log, usertypes from qutebrowser.misc import objects from qutebrowser.commands import cmdexc, cmdutils -from qutebrowser.completion.models import configmodel +from qutebrowser.completion.models import configmodel, miscmodels # An easy way to access the config from other code via config.val.foo val = None @@ -311,7 +311,7 @@ class ConfigCommands: @cmdutils.register(instance='config-commands', maxsplit=1, no_cmd_split=True, no_replace_variables=True) - @cmdutils.argument('command', completion=configmodel.bind) + @cmdutils.argument('command', completion=miscmodels.bind) def bind(self, key, command=None, *, mode='normal', force=False): """Bind a key to a command. diff --git a/tests/end2end/features/completion.feature b/tests/end2end/features/completion.feature index e93518199..d43a46195 100644 --- a/tests/end2end/features/completion.feature +++ b/tests/end2end/features/completion.feature @@ -61,11 +61,11 @@ Feature: Using completion Then the error "Session hello not found!" should be shown Scenario: Using option completion - When I run :set-cmd-text -s :set colors + When I run :set-cmd-text -s :set Then the completion model should be option Scenario: Using value completion - When I run :set-cmd-text -s :set colors statusbar.bg + When I run :set-cmd-text -s :set aliases Then the completion model should be value Scenario: Deleting an open tab via the completion diff --git a/tests/helpers/stubs.py b/tests/helpers/stubs.py index e7b389792..d7dcb427f 100644 --- a/tests/helpers/stubs.py +++ b/tests/helpers/stubs.py @@ -421,16 +421,6 @@ class FakeYamlConfig: self.loaded = True -class FakeConfigType: - - """A stub to provide valid_values for typ attribute of a SettingValue.""" - - def __init__(self, *valid_values): - # normally valid_values would be a ValidValues, but for simplicity of - # testing this can be a simple list: [(val, desc), (val, desc), ...] - self.complete = lambda: [(val, '') for val in valid_values] - - class StatusBarCommandStub(QLineEdit): """Stub for the statusbar command prompt.""" diff --git a/tests/unit/completion/test_completer.py b/tests/unit/completion/test_completer.py index 74b2e51f5..2094eeda8 100644 --- a/tests/unit/completion/test_completer.py +++ b/tests/unit/completion/test_completer.py @@ -29,10 +29,6 @@ from qutebrowser.completion import completer from qutebrowser.commands import command, cmdutils -pytestmark = pytest.mark.skip("FIXME:conf reintroduce after new completion " - "is in") - - class FakeCompletionModel(QStandardItemModel): """Stub for a completion model.""" @@ -67,8 +63,8 @@ def completer_obj(qtbot, status_command_stub, config_stub, monkeypatch, stubs, completion_widget_stub): """Create the completer used for testing.""" monkeypatch.setattr(completer, 'QTimer', stubs.InstaTimer) - config_stub.data = {'completion': {'show': 'auto'}} - return completer.Completer(status_command_stub, 0, completion_widget_stub) + config_stub.val.completion.show = 'auto' + return completer.Completer(status_command_stub, completion_widget_stub) @pytest.fixture(autouse=True) @@ -246,14 +242,14 @@ def test_on_selection_changed(before, newtxt, after, completer_obj, """Test that on_selection_changed modifies the cmd text properly. The | represents the current cursor position in the cmd prompt. - If quick-complete is True and there is only 1 completion (count == 1), + If quick is True and there is only 1 completion (count == 1), then we expect a space to be appended after the current word. """ model = unittest.mock.Mock() completion_widget_stub.model.return_value = model - def check(quick_complete, count, expected_txt, expected_pos): - config_stub.data['completion']['quick-complete'] = quick_complete + def check(quick, count, expected_txt, expected_pos): + config_stub.val.completion.quick = quick model.count = lambda: count _set_cmd_prompt(status_command_stub, before) completer_obj.on_selection_changed(newtxt) @@ -288,7 +284,7 @@ def test_quickcomplete_flicker(status_command_stub, completer_obj, model = unittest.mock.Mock() model.count = unittest.mock.Mock(return_value=1) completion_widget_stub.model.return_value = model - config_stub.data['completion']['quick-complete'] = True + config_stub.val.completion.quick = True _set_cmd_prompt(status_command_stub, ':open |') completer_obj.on_selection_changed('http://example.com') diff --git a/tests/unit/completion/test_histcategory.py b/tests/unit/completion/test_histcategory.py index 1397b8b5f..8ae3bb1f4 100644 --- a/tests/unit/completion/test_histcategory.py +++ b/tests/unit/completion/test_histcategory.py @@ -29,8 +29,8 @@ from qutebrowser.completion.models import histcategory @pytest.fixture def hist(init_sql, config_stub): - config_stub.data['completion'] = {'timestamp-format': '%Y-%m-%d', - 'web-history-max-items': -1} + config_stub.val.completion.timestamp_format = '%Y-%m-%d' + config_stub.val.completion.web_history_max_items = -1 return sql.SqlTable('CompletionHistory', ['url', 'title', 'last_atime']) @@ -129,7 +129,7 @@ def test_set_pattern(pattern, before, after, model_validator, hist): ]) def test_sorting(max_items, before, after, model_validator, hist, config_stub): """Validate the filtering and sorting results of set_pattern.""" - config_stub.data['completion']['web-history-max-items'] = max_items + config_stub.val.completion.web_history_max_items = max_items for url, title, atime in before: timestamp = datetime.datetime.strptime(atime, '%Y-%m-%d').timestamp() hist.insert({'url': url, 'title': title, 'last_atime': timestamp}) diff --git a/tests/unit/completion/test_models.py b/tests/unit/completion/test_models.py index bc2f276af..fca107018 100644 --- a/tests/unit/completion/test_models.py +++ b/tests/unit/completion/test_models.py @@ -29,8 +29,10 @@ import pytest from PyQt5.QtCore import QUrl from qutebrowser.completion.models import miscmodels, urlmodel, configmodel -from qutebrowser.config import sections, value, configdata, configtype +from qutebrowser.config import configdata, configtypes from qutebrowser.utils import objreg +from qutebrowser.browser import history +from qutebrowser.commands import cmdutils def _check_completions(model, expected): @@ -61,60 +63,69 @@ def _check_completions(model, expected): assert sum(model.column_widths) == 100 -def _patch_cmdutils(monkeypatch, stubs, symbol): +@pytest.fixture() +def cmdutils_stub(monkeypatch, stubs): """Patch the cmdutils module to provide fake commands.""" - cmd_utils = stubs.FakeCmdUtils({ - 'stop': stubs.FakeCommand(name='stop', desc='stop qutebrowser'), - 'drop': stubs.FakeCommand(name='drop', desc='drop all user data'), - 'roll': stubs.FakeCommand(name='roll', desc='never gonna give you up'), - 'hide': stubs.FakeCommand(name='hide', hide=True), - 'depr': stubs.FakeCommand(name='depr', deprecated=True), + return monkeypatch.setattr(cmdutils, 'cmd_dict', { + 'quit': stubs.FakeCommand(name='quit', desc='quit qutebrowser'), + 'open': stubs.FakeCommand(name='open', desc='open a url'), + 'prompt-yes': stubs.FakeCommand(name='prompt-yes', deprecated=True), + 'scroll': stubs.FakeCommand(name='scroll', + desc='Scroll the current tab in the given direction.', + hide=True), }) - monkeypatch.setattr(symbol, cmd_utils) -def _patch_configdata(monkeypatch, stubs, symbol): +@pytest.fixture() +def configdata_stub(monkeypatch, configdata_init): """Patch the configdata module to provide fake data.""" - data = collections.OrderedDict([ - ('general.time', configdata.Option( - name='general.time' - ('time', - value.SettingValue(stubs.FakeConfigType('fast', 'slow'), - default='slow'), - 'Is an illusion.\n\nLunchtime doubly so.'), - ('volume', - value.SettingValue(stubs.FakeConfigType('0', '11'), - default='11'), - 'Goes to 11'))), - ('ui', sections.KeyValue( - ('gesture', - value.SettingValue(stubs.FakeConfigType(('on', 'off')), - default='off'), - 'Waggle your hands to control qutebrowser'), - ('mind', - value.SettingValue(stubs.FakeConfigType(('on', 'off')), - default='off'), - 'Enable mind-control ui (experimental)'), - ('voice', - value.SettingValue(stubs.FakeConfigType(('on', 'off')), - default='off'), - 'Whether to respond to voice commands'))), - ('searchengines', sections.ValueList( - stubs.FakeConfigType(), stubs.FakeConfigType(), - ('DEFAULT', 'https://duckduckgo.com/?q={}'), - )), - ]) - monkeypatch.setattr(symbol, data) - - -def _patch_config_section_desc(monkeypatch, stubs, symbol): - """Patch the configdata module to provide fake SECTION_DESC.""" - section_desc = { - 'general': 'General/miscellaneous options.', - 'ui': 'General options related to the user interface.', - 'searchengines': 'Definitions of search engines ...', - } - monkeypatch.setattr(symbol, section_desc) + return monkeypatch.setattr(configdata, 'DATA', collections.OrderedDict([ + ('aliases', configdata.Option( + name='aliases', + description='Aliases for commands.', + typ=configtypes.Dict( + keytype=configtypes.String(), + valtype=configtypes.Command(), + ), + default={'q': 'quit'}, + backends=[], + raw_backends=None)), + ('bindings.default', configdata.Option( + name='bindings.default', + description='Default keybindings', + typ=configtypes.Dict( + keytype=configtypes.String(), + valtype=configtypes.Dict( + keytype=configtypes.String(), + valtype=configtypes.Command(), + ), + ), + default={ + 'normal': { + '': 'quit' + } + }, + backends=[], + raw_backends=None)), + ('bindings.commands', configdata.Option( + name='bindings.commands', + description='Default keybindings', + typ=configtypes.Dict( + keytype=configtypes.String(), + valtype=configtypes.Dict( + keytype=configtypes.String(), + valtype=configtypes.Command(), + ), + ), + default={ + 'normal': { + '': 'quit', + 'ZQ': 'quit' + } + }, + backends=[], + raw_backends=None)), + ])) @pytest.fixture @@ -142,8 +153,8 @@ def bookmarks(bookmark_manager_stub): @pytest.fixture def web_history(init_sql, stubs, config_stub): """Fixture which provides a web-history object.""" - config_stub.completion.timestamp_format = '%Y-%m-%d' - config_stub.completion.web_history_max_items = -1 + config_stub.val.completion.timestamp_format = '%Y-%m-%d' + config_stub.val.completion.web_history_max_items = -1 stub = history.WebHistory() objreg.register('web-history', stub) yield stub @@ -171,7 +182,7 @@ def web_history_populated(web_history): return web_history -def test_command_completion(qtmodeltester, monkeypatch, stubs, config_stub, +def test_command_completion(qtmodeltester, cmdutils_stub, configdata_stub, key_config_stub): """Test the results of command completion. @@ -181,12 +192,6 @@ def test_command_completion(qtmodeltester, monkeypatch, stubs, config_stub, - the binding (if any) is shown in the misc column - aliases are included """ - _patch_cmdutils(monkeypatch, stubs, - 'qutebrowser.completion.models.miscmodels.cmdutils') - config_stub.aliases = {'rock': 'roll'} - key_config_stub.set_bindings_for('normal', {'s': 'stop', - 'rr': 'roll', - 'ro': 'rock'}) model = miscmodels.command() model.set_pattern('') qtmodeltester.data_display_may_return_none = True @@ -194,28 +199,24 @@ def test_command_completion(qtmodeltester, monkeypatch, stubs, config_stub, _check_completions(model, { "Commands": [ - ('drop', 'drop all user data', ''), - ('rock', "Alias for 'roll'", 'ro'), - ('roll', 'never gonna give you up', 'rr'), - ('stop', 'stop qutebrowser', 's'), + ('open', 'open a url', ''), + ('q', "Alias for 'quit'", ''), + ('quit', 'quit qutebrowser', 'ZQ, '), ] }) -def test_help_completion(qtmodeltester, monkeypatch, stubs, key_config_stub): +def test_help_completion(qtmodeltester, cmdutils_stub, key_config_stub, + configdata_stub, config_stub): """Test the results of command completion. Validates that: - only non-deprecated commands are included - the command description is shown in the desc column - the binding (if any) is shown in the misc column - - aliases are included + - aliases are not included - only the first line of a multiline description is shown """ - module = 'qutebrowser.completion.models.miscmodels' - key_config_stub.set_bindings_for('normal', {'s': 'stop', 'rr': 'roll'}) - _patch_cmdutils(monkeypatch, stubs, module + '.cmdutils') - _patch_configdata(monkeypatch, stubs, module + '.configdata.DATA') model = miscmodels.helptopic() model.set_pattern('') qtmodeltester.data_display_may_return_none = True @@ -223,18 +224,14 @@ def test_help_completion(qtmodeltester, monkeypatch, stubs, key_config_stub): _check_completions(model, { "Commands": [ - (':drop', 'drop all user data', ''), - (':hide', '', ''), - (':roll', 'never gonna give you up', 'rr'), - (':stop', 'stop qutebrowser', 's'), + (':open', 'open a url', ''), + (':quit', 'quit qutebrowser', 'ZQ, '), + (':scroll', 'Scroll the current tab in the given direction.', '') ], "Settings": [ - ('general->time', 'Is an illusion.', None), - ('general->volume', 'Goes to 11', None), - ('searchengines->DEFAULT', '', None), - ('ui->gesture', 'Waggle your hands to control qutebrowser', None), - ('ui->mind', 'Enable mind-control ui (experimental)', None), - ('ui->voice', 'Whether to respond to voice commands', None), + ('aliases', 'Aliases for commands.', None), + ('bindings.commands', 'Default keybindings', None), + ('bindings.default', 'Default keybindings', None), ] }) @@ -444,7 +441,7 @@ def test_url_completion_delete_history(qtmodeltester, def test_url_completion_zero_limit(config_stub, web_history, quickmarks, bookmarks): """Make sure there's no history if the limit was set to zero.""" - config_stub.completion.web_history_max_items = 0 + config_stub.val.completion.web_history_max_items = 0 model = urlmodel.url() model.set_pattern('') category = model.index(2, 0) # "History" normally @@ -521,80 +518,49 @@ def test_tab_completion_delete(qtmodeltester, fake_web_tab, app_stub, QUrl('https://duckduckgo.com')] -def test_setting_option_completion(qtmodeltester, monkeypatch, stubs): - module = 'qutebrowser.completion.models.configmodel' - _patch_configdata(monkeypatch, stubs, module + '.configdata.DATA') - model = configmodel.option('ui') +def test_setting_option_completion(qtmodeltester, config_stub, + configdata_stub): + model = configmodel.option() model.set_pattern('') qtmodeltester.data_display_may_return_none = True qtmodeltester.check(model) _check_completions(model, { - "ui": [ - ('gesture', 'Waggle your hands to control qutebrowser', 'off'), - ('mind', 'Enable mind-control ui (experimental)', 'on'), - ('voice', 'Whether to respond to voice commands', 'sometimes'), + "Options": [ + ('aliases', 'Aliases for commands.', '{"q": "quit"}'), + ('bindings.commands', 'Default keybindings', + '{"normal": {"": "quit", "ZQ": "quit"}}'), + ('bindings.default', 'Default keybindings', + '{"normal": {"": "quit"}}'), ] }) -def test_setting_option_completion_empty(monkeypatch, stubs, config_stub): - module = 'qutebrowser.completion.models.configmodel' - _patch_configdata(monkeypatch, stubs, module + '.configdata.DATA') - assert configmodel.option('typo') is None - - -def test_setting_option_completion_valuelist(qtmodeltester, monkeypatch, stubs, - config_stub): - module = 'qutebrowser.completion.models.configmodel' - _patch_configdata(monkeypatch, stubs, module + '.configdata.DATA') - config_stub.data = { - 'searchengines': { - 'DEFAULT': 'https://duckduckgo.com/?q={}' - } - } - model = configmodel.option('searchengines') - model.set_pattern('') - qtmodeltester.data_display_may_return_none = True - qtmodeltester.check(model) - - _check_completions(model, { - 'searchengines': [('DEFAULT', '', 'https://duckduckgo.com/?q={}')] - }) - - -def test_bind_completion(qtmodeltester, monkeypatch, stubs, config_stub, - key_config_stub): +def test_bind_completion(qtmodeltester, cmdutils_stub, config_stub, + key_config_stub, configdata_stub): """Test the results of keybinding command completion. Validates that: - - only non-hidden and non-deprecated commands are included + - only non-deprecated commands are included - the command description is shown in the desc column - the binding (if any) is shown in the misc column - aliases are included """ - _patch_cmdutils(monkeypatch, stubs, - 'qutebrowser.completion.models.miscmodels.cmdutils') - config_stub.data['aliases'] = {'rock': 'roll'} - key_config_stub.set_bindings_for('normal', {'s': 'stop now', - 'rr': 'roll', - 'ro': 'rock'}) - model = miscmodels.bind('s') + model = miscmodels.bind('ZQ') model.set_pattern('') qtmodeltester.data_display_may_return_none = True qtmodeltester.check(model) _check_completions(model, { "Current": [ - ('stop now', 'stop qutebrowser', 's'), + ('quit', 'quit qutebrowser', 'ZQ'), ], "Commands": [ - ('drop', 'drop all user data', ''), - ('hide', '', ''), - ('rock', "Alias for 'roll'", 'ro'), - ('roll', 'never gonna give you up', 'rr'), - ('stop', 'stop qutebrowser', ''), - ] + ('open', 'open a url', ''), + ('q', "Alias for 'quit'", ''), + ('quit', 'quit qutebrowser', 'ZQ, '), + ('scroll', 'Scroll the current tab in the given direction.', '') + ], })