diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index d28efa7e6..9f93d26bd 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -28,6 +28,8 @@ Breaking changes - New dependency on ruamel.yaml; dropped PyYAML dependency. - The QtWebEngine backend is now used by default if available. - New config system which ignores the old config file. +- The depedency on PyOpenGL (when using QtWebEngine) got removed. Note + that PyQt5.QtOpenGL is still a dependency. Major changes ~~~~~~~~~~~~~ diff --git a/CONTRIBUTING.asciidoc b/CONTRIBUTING.asciidoc index fc039ad03..7d42cad28 100644 --- a/CONTRIBUTING.asciidoc +++ b/CONTRIBUTING.asciidoc @@ -45,8 +45,8 @@ pointers: * https://github.com/qutebrowser/qutebrowser/labels/easy[Issues which should be easy to solve] -* https://github.com/qutebrowser/qutebrowser/labels/not%20code[Issues which -require little/no coding] +* https://github.com/qutebrowser/qutebrowser/labels/component%3A%20docs[Documentation +* issues which require little/no coding] If you prefer C++ or Javascript to Python, see the relevant issues which involve work in those languages: diff --git a/README.asciidoc b/README.asciidoc index f7ad0f64d..bf6225062 100644 --- a/README.asciidoc +++ b/README.asciidoc @@ -118,7 +118,6 @@ The following software and libraries are required to run qutebrowser: * http://jinja.pocoo.org/[jinja2] * http://pygments.org/[pygments] * http://pyyaml.org/wiki/PyYAML[PyYAML] -* http://pyopengl.sourceforge.net/[PyOpenGL] when using QtWebEngine The following libraries are optional: diff --git a/misc/requirements/requirements-codecov.txt b/misc/requirements/requirements-codecov.txt index 86f78562d..9d6737a96 100644 --- a/misc/requirements/requirements-codecov.txt +++ b/misc/requirements/requirements-codecov.txt @@ -1,9 +1,9 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -certifi==2017.4.17 +certifi==2017.7.27.1 chardet==3.0.4 codecov==2.0.9 coverage==4.4.1 idna==2.5 -requests==2.18.1 -urllib3==1.21.1 +requests==2.18.2 +urllib3==1.22 diff --git a/misc/requirements/requirements-flake8.txt b/misc/requirements/requirements-flake8.txt index f68bcb227..5e5980525 100644 --- a/misc/requirements/requirements-flake8.txt +++ b/misc/requirements/requirements-flake8.txt @@ -3,7 +3,7 @@ flake8==2.6.2 # rq.filter: < 3.0.0 flake8-copyright==0.2.0 flake8-debugger==1.4.0 # rq.filter: != 2.0.0 -flake8-deprecated==1.2 +flake8-deprecated==1.2.1 flake8-docstrings==1.0.3 # rq.filter: < 1.1.0 flake8-future-import==0.4.3 flake8-mock==0.3 diff --git a/misc/requirements/requirements-pip.txt b/misc/requirements/requirements-pip.txt index 55c1f873e..3b36a0e5c 100644 --- a/misc/requirements/requirements-pip.txt +++ b/misc/requirements/requirements-pip.txt @@ -3,6 +3,6 @@ appdirs==1.4.3 packaging==16.8 pyparsing==2.2.0 -setuptools==36.2.0 +setuptools==36.2.5 six==1.10.0 wheel==0.29.0 diff --git a/misc/requirements/requirements-pylint-master.txt b/misc/requirements/requirements-pylint-master.txt index 37e705b7a..d4058b1d0 100644 --- a/misc/requirements/requirements-pylint-master.txt +++ b/misc/requirements/requirements-pylint-master.txt @@ -1,7 +1,7 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -e git+https://github.com/PyCQA/astroid.git#egg=astroid -certifi==2017.4.17 +certifi==2017.7.27.1 chardet==3.0.4 github3.py==0.9.6 idna==2.5 @@ -10,9 +10,9 @@ lazy-object-proxy==1.3.1 mccabe==0.6.1 -e git+https://github.com/PyCQA/pylint.git#egg=pylint ./scripts/dev/pylint_checkers -requests==2.18.1 +requests==2.18.2 six==1.10.0 uritemplate==3.0.0 uritemplate.py==3.0.2 -urllib3==1.21.1 +urllib3==1.22 wrapt==1.10.10 diff --git a/misc/requirements/requirements-pylint.txt b/misc/requirements/requirements-pylint.txt index a76d0dbf4..b5d44cb64 100644 --- a/misc/requirements/requirements-pylint.txt +++ b/misc/requirements/requirements-pylint.txt @@ -1,7 +1,7 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py astroid==1.5.3 -certifi==2017.4.17 +certifi==2017.7.27.1 chardet==3.0.4 github3.py==0.9.6 idna==2.5 @@ -10,9 +10,9 @@ lazy-object-proxy==1.3.1 mccabe==0.6.1 pylint==1.7.2 ./scripts/dev/pylint_checkers -requests==2.18.1 +requests==2.18.2 six==1.10.0 uritemplate==3.0.0 uritemplate.py==3.0.2 -urllib3==1.21.1 +urllib3==1.22 wrapt==1.10.10 diff --git a/misc/requirements/requirements-tests.txt b/misc/requirements/requirements-tests.txt index 91cecb798..171013afb 100644 --- a/misc/requirements/requirements-tests.txt +++ b/misc/requirements/requirements-tests.txt @@ -5,14 +5,14 @@ cheroot==5.7.0 click==6.7 # colorama==0.3.9 coverage==4.4.1 -decorator==4.1.1 +decorator==4.1.2 EasyProcess==0.2.3 fields==5.0.0 Flask==0.12.2 glob2==0.5 httpbin==0.5.0 hunter==1.4.1 -hypothesis==3.13.0 +hypothesis==3.14.0 itsdangerous==0.24 # Jinja2==2.9.6 Mako==1.0.7 @@ -22,12 +22,12 @@ parse-type==0.3.4 py==1.4.34 pytest==3.1.3 pytest-bdd==2.18.2 -pytest-benchmark==3.0.0 +pytest-benchmark==3.1.1 pytest-catchlog==1.2.2 pytest-cov==2.5.1 pytest-faulthandler==1.3.1 pytest-instafail==0.3.0 -pytest-mock==1.6.0 +pytest-mock==1.6.2 pytest-qt==2.1.2 pytest-repeat==0.4.1 pytest-rerunfailures==2.2 @@ -35,5 +35,5 @@ pytest-travis-fold==1.2.0 pytest-xvfb==1.0.0 PyVirtualDisplay==0.2.1 six==1.10.0 -vulture==0.16 +vulture==0.21 Werkzeug==0.12.2 diff --git a/misc/requirements/requirements-vulture.txt b/misc/requirements/requirements-vulture.txt index f2ba7dcbf..d1c1bc41d 100644 --- a/misc/requirements/requirements-vulture.txt +++ b/misc/requirements/requirements-vulture.txt @@ -1,3 +1,3 @@ # This file is automatically generated by scripts/dev/recompile_requirements.py -vulture==0.16 +vulture==0.21 diff --git a/pytest.ini b/pytest.ini index 225d64d9a..08273ef8d 100644 --- a/pytest.ini +++ b/pytest.ini @@ -1,5 +1,5 @@ [pytest] -addopts = --strict -rfEw --faulthandler-timeout=70 --instafail --pythonwarnings error +addopts = --strict -rfEw --faulthandler-timeout=70 --instafail --pythonwarnings error --benchmark-columns=Min,Max,Median testpaths = tests markers = gui: Tests using the GUI (e.g. spawning widgets) diff --git a/qutebrowser/browser/urlmarks.py b/qutebrowser/browser/urlmarks.py index 013de408c..b7c93a994 100644 --- a/qutebrowser/browser/urlmarks.py +++ b/qutebrowser/browser/urlmarks.py @@ -77,13 +77,9 @@ class UrlMarkManager(QObject): Signals: changed: Emitted when anything changed. - added: Emitted when a new quickmark/bookmark was added. - removed: Emitted when an existing quickmark/bookmark was removed. """ changed = pyqtSignal() - added = pyqtSignal(str, str) - removed = pyqtSignal(str) def __init__(self, parent=None): """Initialize and read quickmarks.""" @@ -121,7 +117,6 @@ class UrlMarkManager(QObject): """ del self.marks[key] self.changed.emit() - self.removed.emit(key) class QuickmarkManager(UrlMarkManager): @@ -133,7 +128,6 @@ class QuickmarkManager(UrlMarkManager): - self.marks maps names to URLs. - changed gets emitted with the name as first argument and the URL as second argument. - - removed gets emitted with the name as argument. """ def _init_lineparser(self): @@ -193,7 +187,6 @@ class QuickmarkManager(UrlMarkManager): """Really set the quickmark.""" self.marks[name] = url self.changed.emit() - self.added.emit(name, url) log.misc.debug("Added quickmark {} for {}".format(name, url)) if name in self.marks: @@ -243,7 +236,6 @@ class BookmarkManager(UrlMarkManager): - self.marks maps URLs to titles. - changed gets emitted with the URL as first argument and the title as second argument. - - removed gets emitted with the URL as argument. """ def _init_lineparser(self): @@ -295,5 +287,4 @@ class BookmarkManager(UrlMarkManager): else: self.marks[urlstr] = title self.changed.emit() - self.added.emit(title, urlstr) return True diff --git a/qutebrowser/browser/webengine/webenginesettings.py b/qutebrowser/browser/webengine/webenginesettings.py index 27ef60cb6..1148dbad9 100644 --- a/qutebrowser/browser/webengine/webenginesettings.py +++ b/qutebrowser/browser/webengine/webenginesettings.py @@ -28,6 +28,9 @@ Module attributes: """ import os +import sys +import ctypes +import ctypes.util from PyQt5.QtGui import QFont from PyQt5.QtWebEngineWidgets import (QWebEngineSettings, QWebEngineProfile, @@ -199,6 +202,11 @@ def init(args): if args.enable_webengine_inspector: os.environ['QTWEBENGINE_REMOTE_DEBUGGING'] = str(utils.random_port()) + # WORKAROUND for + # https://bugs.launchpad.net/ubuntu/+source/python-qt4/+bug/941826 + if sys.platform == 'linux': + ctypes.CDLL(ctypes.util.find_library("GL"), mode=ctypes.RTLD_GLOBAL) + _init_profiles() # We need to do this here as a WORKAROUND for diff --git a/qutebrowser/completion/completionwidget.py b/qutebrowser/completion/completionwidget.py index 65f1de76c..7bf685498 100644 --- a/qutebrowser/completion/completionwidget.py +++ b/qutebrowser/completion/completionwidget.py @@ -148,6 +148,8 @@ class CompletionView(QTreeView): def _resize_columns(self): """Resize the completion columns based on column_widths.""" + if self.model() is None: + return width = self.size().width() column_widths = self.model().column_widths pixel_widths = [(width * perc // 100) for perc in column_widths] @@ -253,6 +255,10 @@ class CompletionView(QTreeView): selmodel.setCurrentIndex( idx, QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows) + # if the last item is focused, try to fetch more + if idx.row() == self.model().rowCount(idx.parent()) - 1: + self.expandAll() + count = self.model().count() if count == 0: self.hide() diff --git a/qutebrowser/completion/models/histcategory.py b/qutebrowser/completion/models/histcategory.py index 42301c0dd..606351440 100644 --- a/qutebrowser/completion/models/histcategory.py +++ b/qutebrowser/completion/models/histcategory.py @@ -93,10 +93,12 @@ class HistoryCategory(QSqlQueryModel): self._query.run(pat=pattern) self.setQuery(self._query) - def removeRows(self, _row, _count, _parent=None): + def removeRows(self, row, _count, _parent=None): """Override QAbstractItemModel::removeRows to re-run sql query.""" # re-run query to reload updated table with debug.log_time('sql', 'Re-running completion query post-delete'): self._query.run() self.setQuery(self._query) + while self.rowCount() < row: + self.fetchMore() return True diff --git a/qutebrowser/completion/models/miscmodels.py b/qutebrowser/completion/models/miscmodels.py index 167eccde8..a61b73ffa 100644 --- a/qutebrowser/completion/models/miscmodels.py +++ b/qutebrowser/completion/models/miscmodels.py @@ -60,17 +60,33 @@ def helptopic(): def quickmark(): """A CompletionModel filled with all quickmarks.""" + def delete(data): + """Delete a quickmark from the completion menu.""" + name = data[0] + quickmark_manager = objreg.get('quickmark-manager') + log.completion.debug('Deleting quickmark {}'.format(name)) + quickmark_manager.delete(name) + model = completionmodel.CompletionModel(column_widths=(30, 70, 0)) marks = objreg.get('quickmark-manager').marks.items() - model.add_category(listcategory.ListCategory('Quickmarks', marks)) + model.add_category(listcategory.ListCategory('Quickmarks', marks, + delete_func=delete)) return model def bookmark(): """A CompletionModel filled with all bookmarks.""" + def delete(data): + """Delete a bookmark from the completion menu.""" + urlstr = data[0] + log.completion.debug('Deleting bookmark {}'.format(urlstr)) + bookmark_manager = objreg.get('bookmark-manager') + bookmark_manager.delete(urlstr) + model = completionmodel.CompletionModel(column_widths=(30, 70, 0)) marks = objreg.get('bookmark-manager').marks.items() - model.add_category(listcategory.ListCategory('Bookmarks', marks)) + model.add_category(listcategory.ListCategory('Bookmarks', marks, + delete_func=delete)) return model @@ -126,11 +142,12 @@ def bind(key): key: the key being bound. """ model = completionmodel.CompletionModel(column_widths=(20, 60, 20)) - cmd_name = objreg.get('key-config').get_bindings_for('normal').get(key) + cmd_text = objreg.get('key-config').get_bindings_for('normal').get(key) - if cmd_name: + if cmd_text: + cmd_name = cmd_text.split(' ')[0] cmd = cmdutils.cmd_dict.get(cmd_name) - data = [(cmd_name, cmd.desc, key)] + data = [(cmd_text, cmd.desc, key)] model.add_category(listcategory.ListCategory("Current", data)) cmdlist = _get_cmd_completions(include_hidden=True, include_aliases=True) diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index d74f74b4a..50758a025 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -708,13 +708,16 @@ class TabbedBrowser(tabwidget.TabWidget): } msg = messages[status] + def show_error_page(html): + tab.set_html(html) + log.webview.error(msg) + if qtutils.version_check('5.9'): url_string = tab.url(requested=True).toDisplayString() error_page = jinja.render( 'error.html', title="Error loading {}".format(url_string), url=url_string, error=msg, icon='') - QTimer.singleShot(0, lambda: tab.set_html(error_page)) - log.webview.error(msg) + QTimer.singleShot(100, lambda: show_error_page(error_page)) else: # WORKAROUND for https://bugreports.qt.io/browse/QTBUG-58698 message.error(msg) diff --git a/qutebrowser/misc/earlyinit.py b/qutebrowser/misc/earlyinit.py index e43cd8891..a64a2799b 100644 --- a/qutebrowser/misc/earlyinit.py +++ b/qutebrowser/misc/earlyinit.py @@ -36,7 +36,6 @@ import traceback import signal import importlib import datetime -import logging try: import tkinter except ImportError: @@ -344,12 +343,6 @@ def check_libraries(backend): modules['PyQt5.QtWebEngineWidgets'] = _missing_str("QtWebEngine", webengine=True) modules['PyQt5.QtOpenGL'] = _missing_str("PyQt5.QtOpenGL") - # Workaround for a black screen with some setups - # https://github.com/spyder-ide/spyder/issues/3226 - if not os.environ.get('QUTE_NO_OPENGL_WORKAROUND'): - # Hide "No OpenGL_accelerate module loaded: ..." message - logging.getLogger('OpenGL.acceleratesupport').propagate = False - modules['OpenGL.GL'] = _missing_str("PyOpenGL") else: assert backend == 'webkit' modules['PyQt5.QtWebKit'] = _missing_str("PyQt5.QtWebKit") diff --git a/qutebrowser/misc/sessions.py b/qutebrowser/misc/sessions.py index 4fe0fe4c7..0a4fae5b7 100644 --- a/qutebrowser/misc/sessions.py +++ b/qutebrowser/misc/sessions.py @@ -23,7 +23,7 @@ import os import os.path import sip -from PyQt5.QtCore import pyqtSignal, QUrl, QObject, QPoint, QTimer +from PyQt5.QtCore import QUrl, QObject, QPoint, QTimer from PyQt5.QtWidgets import QApplication import yaml try: @@ -106,14 +106,8 @@ class SessionManager(QObject): closed. _current: The name of the currently loaded session, or None. did_load: Set when a session was loaded. - - Signals: - update_completion: Emitted when the session completion should get - updated. """ - update_completion = pyqtSignal() - def __init__(self, base_path, parent=None): super().__init__(parent) self._current = None @@ -303,8 +297,7 @@ class SessionManager(QObject): encoding='utf-8', allow_unicode=True) except (OSError, UnicodeEncodeError, yaml.YAMLError) as e: raise SessionError(e) - else: - self.update_completion.emit() + if load_next_time: state_config = objreg.get('state-config') state_config['general']['session'] = name @@ -425,7 +418,6 @@ class SessionManager(QObject): os.remove(path) except OSError as e: raise SessionError(e) - self.update_completion.emit() def list_sessions(self): """Get a list of all session names.""" diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py index fe3e1aedb..0b650a97e 100644 --- a/qutebrowser/utils/version.py +++ b/qutebrowser/utils/version.py @@ -186,7 +186,6 @@ def _module_versions(): ('yaml', ['__version__']), ('cssutils', ['__version__']), ('typing', []), - ('OpenGL', ['__version__']), ('PyQt5.QtWebEngineWidgets', []), ('PyQt5.QtWebKitWidgets', []), ]) diff --git a/requirements.txt b/requirements.txt index cbf9ba407..b2cc93c1f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,4 +7,3 @@ MarkupSafe==1.0 Pygments==2.2.0 pyPEG2==2.15.2 PyYAML==3.12 -PyOpenGL==3.1.0 diff --git a/scripts/dev/run_vulture.py b/scripts/dev/run_vulture.py index 8a1886e11..2a0ecec44 100755 --- a/scripts/dev/run_vulture.py +++ b/scripts/dev/run_vulture.py @@ -89,6 +89,12 @@ def whitelist_generator(): # vulture doesn't notice the hasattr() and thus thinks netrc_used is unused # in NetworkManager.on_authentication_required yield 'PyQt5.QtNetwork.QNetworkReply.netrc_used' + yield 'qutebrowser.browser.downloads.last_used_directory' + yield 'PaintContext.clip' # from completiondelegate.py + yield 'logging.LogRecord.log_color' # from logging.py + yield 'scripts.utils.use_color' # from asciidoc2html.py + for attr in ['pyeval_output', 'log_clipboard', 'fake_clipboard']: + yield 'qutebrowser.misc.utilcmds.' + attr for attr in ['fileno', 'truncate', 'closed', 'readable']: yield 'qutebrowser.utils.qtutils.PyQIODevice.' + attr @@ -111,7 +117,7 @@ def filter_func(item): True if the missing function should be filtered/ignored, False otherwise. """ - return bool(re.match(r'[a-z]+[A-Z][a-zA-Z]+', str(item))) + return bool(re.match(r'[a-z]+[A-Z][a-zA-Z]+', item.name)) def report(items): @@ -125,7 +131,7 @@ def report(items): relpath = os.path.relpath(item.filename) path = relpath if not relpath.startswith('..') else item.filename output.append("{}:{}: Unused {} '{}'".format(path, item.lineno, - item.typ, item)) + item.typ, item.name)) return output diff --git a/tests/unit/completion/test_completionwidget.py b/tests/unit/completion/test_completionwidget.py index 22d150fd8..ddbc13c77 100644 --- a/tests/unit/completion/test_completionwidget.py +++ b/tests/unit/completion/test_completionwidget.py @@ -65,6 +65,9 @@ def completionview(qtbot, status_command_stub, config_stub, win_registry, } # mock the Completer that the widget creates in its constructor mocker.patch('qutebrowser.completion.completer.Completer', autospec=True) + mocker.patch( + 'qutebrowser.completion.completiondelegate.CompletionItemDelegate', + new=lambda *_: None) view = completionwidget.CompletionView(win_id=0) qtbot.addWidget(view) return view @@ -186,6 +189,37 @@ def test_completion_item_focus_no_model(which, completionview, qtbot): completionview.completion_item_focus(which) +def test_completion_item_focus_fetch(completionview, qtbot): + """Test that on_next_prev_item moves the selection properly. + + Args: + which: the direction in which to move the selection. + tree: Each list represents a completion category, with each string + being an item under that category. + expected: expected argument from on_selection_changed for each + successive movement. None implies no signal should be + emitted. + """ + model = completionmodel.CompletionModel() + cat = mock.Mock(spec=['layoutChanged', 'layoutAboutToBeChanged', + 'canFetchMore', 'fetchMore', 'rowCount', 'index', 'data']) + cat.canFetchMore = lambda *_: True + cat.rowCount = lambda *_: 2 + cat.fetchMore = mock.Mock() + model.add_category(cat) + completionview.set_model(model) + # clear the fetchMore call that happens on set_model + cat.reset_mock() + + # not at end, fetchMore shouldn't be called + completionview.completion_item_focus('next') + assert not cat.fetchMore.called + + # at end, fetchMore should be called + completionview.completion_item_focus('next') + assert cat.fetchMore.called + + @pytest.mark.parametrize('show', ['always', 'auto', 'never']) @pytest.mark.parametrize('rows', [[], ['Aa'], ['Aa', 'Bb']]) @pytest.mark.parametrize('quick_complete', [True, False]) @@ -240,3 +274,8 @@ def test_completion_item_del_no_selection(completionview): with pytest.raises(cmdexc.CommandError, match='No item selected!'): completionview.completion_item_del() assert not func.called + + +def test_resize_no_model(completionview, qtbot): + """Ensure no crash if resizeEvent is triggered with no model (#2854).""" + completionview.resizeEvent(None) diff --git a/tests/unit/completion/test_histcategory.py b/tests/unit/completion/test_histcategory.py index db4071502..1397b8b5f 100644 --- a/tests/unit/completion/test_histcategory.py +++ b/tests/unit/completion/test_histcategory.py @@ -22,7 +22,6 @@ import datetime import pytest -from PyQt5.QtCore import QModelIndex from qutebrowser.misc import sql from qutebrowser.completion.models import histcategory @@ -147,6 +146,22 @@ def test_remove_rows(hist, model_validator): model_validator.set_model(cat) cat.set_pattern('') hist.delete('url', 'foo') - # histcategory does not care which index was removed, it just regenerates - cat.removeRows(QModelIndex(), 1) + cat.removeRows(0, 1) model_validator.validate([('bar', 'Bar', '')]) + + +def test_remove_rows_fetch(hist): + """removeRows should fetch enough data to make the current index valid.""" + # we cannot use model_validator as it will fetch everything up front + hist.insert_batch({'url': [str(i) for i in range(300)]}) + cat = histcategory.HistoryCategory() + cat.set_pattern('') + + # sanity check that we didn't fetch everything up front + assert cat.rowCount() < 300 + cat.fetchMore() + assert cat.rowCount() == 300 + + hist.delete('url', '298') + cat.removeRows(297, 1) + assert cat.rowCount() == 299 diff --git a/tests/unit/completion/test_models.py b/tests/unit/completion/test_models.py index 83c3d3e7d..b4a940977 100644 --- a/tests/unit/completion/test_models.py +++ b/tests/unit/completion/test_models.py @@ -252,6 +252,27 @@ def test_quickmark_completion(qtmodeltester, quickmarks): }) +@pytest.mark.parametrize('row, removed', [ + (0, 'aw'), + (1, 'ddg'), + (2, 'wiki'), +]) +def test_quickmark_completion_delete(qtmodeltester, quickmarks, row, removed): + """Test deleting a quickmark from the quickmark completion model.""" + model = miscmodels.quickmark() + model.set_pattern('') + qtmodeltester.data_display_may_return_none = True + qtmodeltester.check(model) + + parent = model.index(0, 0) + idx = model.index(row, 0, parent) + + before = set(quickmarks.marks.keys()) + model.delete_cur_item(idx) + after = set(quickmarks.marks.keys()) + assert before.difference(after) == {removed} + + def test_bookmark_completion(qtmodeltester, bookmarks): """Test the results of bookmark completion.""" model = miscmodels.bookmark() @@ -268,6 +289,27 @@ def test_bookmark_completion(qtmodeltester, bookmarks): }) +@pytest.mark.parametrize('row, removed', [ + (0, 'http://qutebrowser.org'), + (1, 'https://github.com'), + (2, 'https://python.org'), +]) +def test_bookmark_completion_delete(qtmodeltester, bookmarks, row, removed): + """Test deleting a quickmark from the quickmark completion model.""" + model = miscmodels.bookmark() + model.set_pattern('') + qtmodeltester.data_display_may_return_none = True + qtmodeltester.check(model) + + parent = model.index(0, 0) + idx = model.index(row, 0, parent) + + before = set(bookmarks.marks.keys()) + model.delete_cur_item(idx) + after = set(bookmarks.marks.keys()) + assert before.difference(after) == {removed} + + def test_url_completion(qtmodeltester, web_history_populated, quickmarks, bookmarks): """Test the results of url completion. @@ -583,7 +625,7 @@ def test_bind_completion(qtmodeltester, monkeypatch, stubs, config_stub, _patch_cmdutils(monkeypatch, stubs, 'qutebrowser.completion.models.miscmodels.cmdutils') config_stub.data['aliases'] = {'rock': 'roll'} - key_config_stub.set_bindings_for('normal', {'s': 'stop', + key_config_stub.set_bindings_for('normal', {'s': 'stop now', 'rr': 'roll', 'ro': 'rock'}) model = miscmodels.bind('s') @@ -593,14 +635,14 @@ def test_bind_completion(qtmodeltester, monkeypatch, stubs, config_stub, _check_completions(model, { "Current": [ - ('stop', 'stop qutebrowser', 's'), + ('stop now', 'stop qutebrowser', 's'), ], "Commands": [ ('drop', 'drop all user data', ''), ('hide', '', ''), ('rock', "Alias for 'roll'", 'ro'), ('roll', 'never gonna give you up', 'rr'), - ('stop', 'stop qutebrowser', 's'), + ('stop', 'stop qutebrowser', ''), ] }) diff --git a/tests/unit/misc/test_sessions.py b/tests/unit/misc/test_sessions.py index e77b6aa02..00311ec8e 100644 --- a/tests/unit/misc/test_sessions.py +++ b/tests/unit/misc/test_sessions.py @@ -215,11 +215,6 @@ class TestSave: objreg.delete('main-window', scope='window', window=0) objreg.delete('tabbed-browser', scope='window', window=0) - def test_update_completion_signal(self, sess_man, tmpdir, qtbot): - session_path = tmpdir / 'foo.yml' - with qtbot.waitSignal(sess_man.update_completion): - sess_man.save(str(session_path)) - def test_no_state_config(self, sess_man, tmpdir, state_config): session_path = tmpdir / 'foo.yml' sess_man.save(str(session_path)) @@ -367,14 +362,6 @@ class TestLoadTab: assert loaded_item.original_url == expected -def test_delete_update_completion_signal(sess_man, qtbot, tmpdir): - sess = tmpdir / 'foo.yml' - sess.ensure() - - with qtbot.waitSignal(sess_man.update_completion): - sess_man.delete(str(sess)) - - class TestListSessions: def test_no_sessions(self, tmpdir): diff --git a/tests/unit/utils/test_version.py b/tests/unit/utils/test_version.py index 339e70987..f9da37841 100644 --- a/tests/unit/utils/test_version.py +++ b/tests/unit/utils/test_version.py @@ -495,7 +495,6 @@ class ImportFake: ('yaml', True), ('cssutils', True), ('typing', True), - ('OpenGL', True), ('PyQt5.QtWebEngineWidgets', True), ('PyQt5.QtWebKitWidgets', True), ])