Compare commits

...

23 Commits

Author SHA1 Message Date
Florian Bruhin
fc63cea917 Release v1.0.3 2017-11-04 16:48:53 +01:00
Florian Bruhin
effbe36919 Also update sip version 2017-11-04 16:35:21 +01:00
Florian Bruhin
db466097e0 Update requirements to PyQt 5.9.1
This will break things as there's no PyQt 5.9.1 Linux wheel available yet, but
it'll make it possible to release a new qutebrowser release for macOS and
Windows with Qt 5.9.2 at least.
2017-11-04 16:10:47 +01:00
Florian Bruhin
0446babb3d Update changelog from master 2017-11-04 16:10:30 +01:00
Florian Bruhin
85d643ff49 Fix unbinding default keys twice
When doing :unbind with a default keybinding the first time, it gets inserted
into bindings.commands with None as value.

When then doing :unbind a second time, instead of just leaving that None value
as-is, we removed it again (because it got treated as a custom binding).

Fixes #3162

(cherry picked from commit 5689a3c0dc)
2017-11-04 15:22:16 +01:00
Ryan Roden-Corrent
12c35ff836 Don't check date string in test_histcategory.
We really just need to check that the row exists here, the date doesn't
matter. Checking the date here is actually flaky with regards to time.
When running locally at 11:50 EST, it failed with:

```
assert self._model.data(self._model.index(row, col)) == item
AssertionError: assert '1969-12-31' == '1970-01-01'
- 1969-12-31
+ 1970-01-01
```

It was wrong to assume that an atime of 0 would always format to
1970-01-01.

(cherry picked from commit a9926e44f0)
2017-11-04 15:21:57 +01:00
Ryan Roden-Corrent
b33bb94593 Ensure completions are sorted after filtering.
I previously removed the sorting logic from SortFilter thinking it was
unnecessary if we construct the model with a sorted list. However, this
only worked when no pattern was set, and the items are misordered as
soon as a pattern is input.

This patch reintroduces alpha-sorting, which can be disabled by passing
sort=False to the ListCategory constructor. The session completion test
had to be tweaked as it simulated the incorrect assumption that the
session list is not alpha-ordered; sessions come out of the
session-manager pre-sorted so we may as well use alpha-sorting in the
session completion model.

Resolves #3156.

(cherry picked from commit 47447c047a)
2017-11-04 15:21:54 +01:00
Florian Bruhin
25a11f492b Un-hide :open-editor
It can be used in normal mode as well, and it's nice to have it discoverable.
Fixes #3235.

(cherry picked from commit bb208f4e77)
2017-11-04 15:21:27 +01:00
Florian Bruhin
d0453542c3 Clarify qute://configdiff/old title
(cherry picked from commit 1c39715267)
2017-11-04 15:18:26 +01:00
Florian Bruhin
4bff63f17b Use lts version of NopeJS
Looks like npm doesn't work with Node v9:
https://github.com/nodejs/node/issues/16649

(cherry picked from commit 385337eb90)
2017-11-02 11:28:24 +01:00
Jay Kamat
a5426ca4ac Abort pinned tab prompt if tab is destroyed
Closes #3223

(cherry picked from commit cb7e6ab02d)
2017-11-02 09:16:03 +01:00
Jay Kamat
ec9981f0d1 Fix a couple style issues
(cherry picked from commit 64b6852ae3)
2017-10-31 07:06:55 +01:00
Jay Kamat
5ebca1c309 Fix ellipsis on pinned tabs with index > 10
See #3209

(cherry picked from commit 2a4163b2c7)
2017-10-31 07:06:55 +01:00
Jay Kamat
1559965e6b Add a simple benchmark for _update_tab_titles
(cherry picked from commit 97d719b179)
2017-10-31 07:06:55 +01:00
Jay Kamat
9a2d451bc5 Lower tabbar cache bound and clean up code
(cherry picked from commit cb6f4313d7)
2017-10-31 07:06:55 +01:00
Jay Kamat
5a080ebea5 Add most recent tab bar to cache statistics
(cherry picked from commit 49daa7aab8)
2017-10-31 07:06:55 +01:00
Jay Kamat
2f3e3c3073 Prevent calling _tab_pinned on every tab twice
(cherry picked from commit b499474599)
2017-10-31 07:06:55 +01:00
Jay Kamat
457bba7e79 Fix blowing cache for different icons
(cherry picked from commit caae1c7008)
2017-10-31 07:06:55 +01:00
Jay Kamat
0045af8738 Clear cache on config changes
(cherry picked from commit fde4495bc7)
2017-10-31 07:06:55 +01:00
Jay Kamat
7dc82407e8 Rename _minimum_tab_size_hint_helper
(cherry picked from commit e705ea7e56)
2017-10-31 07:06:55 +01:00
Jay Kamat
0becdfa6b0 Add caching for tab sizes
(cherry picked from commit 08b562ea0c)
2017-10-31 07:06:36 +01:00
Florian Bruhin
b69b5c81fc Remove -f for :bind in configuring.asciidoc
[ci skip]

(cherry picked from commit 17e0f6d23c)
2017-10-27 07:17:57 +02:00
Florian Bruhin
8607ddce24 Fix error code for "database is locked"
See #2930

(cherry picked from commit 96bec9f9d7)
2017-10-17 15:36:12 +02:00
23 changed files with 175 additions and 72 deletions

View File

@@ -51,7 +51,7 @@ matrix:
env: TESTENV=eslint
language: node_js
python: null
node_js: node
node_js: "lts/*"
fast_finish: true
cache:

View File

@@ -15,10 +15,32 @@ breaking changes (such as renamed commands) can happen in minor releases.
// `Fixed` for any bug fixes.
// `Security` to invite users to upgrade in case of vulnerabilities.
v1.0.3
------
Changed
~~~~~~~
- macOS and Windows builds are now built with PyQt 5.9.1 and Qt 5.9.2, including
various bugfixes, as well as security fixes from Chromium up to version
61.0.3163.79.
- Performance improvements for tab rendering.
- The :open-editor command is now not hidden anymore as it's also usable in
normal mode.
Fixed
~~~~~
- Handle accessing a locked sqlite database gracefully
- Abort pinned tab dialogs properly when a tab is closed e.g. by closing a
window
- Unbinding a default keybinding twice now doesn't bind it again
- Completions are now sorted correctly again when filtered
v1.0.2
------
Fixes
Fixed
~~~~~
- Fix workaround for black screens or crashes with Nvidia cards
@@ -36,7 +58,7 @@ Changed
v1.0.1
------
Fixes
Fixed
~~~~~
- Fixed starting after customizing `fonts.tabs` or `fonts.debug_console`.
@@ -74,6 +96,9 @@ Major changes
the entire browsing history. The default for
`completion.web_history_max_items` got changed to `-1` (unlimited). If the
completion is too slow on your machine, try setting it to a few 1000 items.
- Up/Down now navigates through the command history instead of selecting
completion items. Either use Tab to cycle through the completion, or
https://github.com/qutebrowser/qutebrowser/blob/master/doc/help/configuring.asciidoc#migrating-older-configurations[restore the old behavior].
Added
~~~~~

View File

@@ -60,6 +60,7 @@ It is possible to run or bind multiple commands by separating them with `;;`.
|<<messages,messages>>|Show a log of past messages.
|<<navigate,navigate>>|Open typical prev/next links or navigate using the URL path.
|<<open,open>>|Open a URL in the current/[count]th tab.
|<<open-editor,open-editor>>|Open an external editor with the currently selected form field.
|<<print,print>>|Print the current/[count]th tab.
|<<quickmark-add,quickmark-add>>|Add a new quickmark.
|<<quickmark-del,quickmark-del>>|Delete a quickmark.
@@ -653,6 +654,12 @@ The tab index to open the URL in.
==== note
* This command does not split arguments after the last argument and handles quotes literally.
[[open-editor]]
=== open-editor
Open an external editor with the currently selected form field.
The editor which should be launched can be configured via the `editor.command` config option.
[[print]]
=== print
Syntax: +:print [*--preview*] [*--pdf* 'file']+
@@ -1137,7 +1144,6 @@ How many steps to zoom out.
|<<move-to-start-of-next-block,move-to-start-of-next-block>>|Move the cursor or selection to the start of next block.
|<<move-to-start-of-prev-block,move-to-start-of-prev-block>>|Move the cursor or selection to the start of previous block.
|<<nop,nop>>|Do nothing.
|<<open-editor,open-editor>>|Open an external editor with the currently selected form field.
|<<prompt-accept,prompt-accept>>|Accept the current prompt.
|<<prompt-item-focus,prompt-item-focus>>|Shift the focus of the prompt file completion menu to another item.
|<<prompt-open-download,prompt-open-download>>|Immediately open a download.
@@ -1388,12 +1394,6 @@ How many blocks to move.
=== nop
Do nothing.
[[open-editor]]
=== open-editor
Open an external editor with the currently selected form field.
The editor which should be launched can be configured via the `editor.command` config option.
[[prompt-accept]]
=== prompt-accept
Syntax: +:prompt-accept ['value']+

View File

@@ -22,8 +22,8 @@ Other changes in default settings:
doing:
+
----
:bind -f -m command <Up> completion-item-focus prev
:bind -f -m command <Down> completion-item-focus next
:bind -m command <Up> completion-item-focus prev
:bind -m command <Down> completion-item-focus next
----
- The default for `completion.web_history_max_items` is now set to `-1`, showing

View File

@@ -1,4 +1,4 @@
# This file is automatically generated by scripts/dev/recompile_requirements.py
PyQt5==5.9
sip==4.19.3
PyQt5==5.9.1
sip==4.19.4

View File

@@ -26,7 +26,7 @@ __copyright__ = "Copyright 2014-2017 Florian Bruhin (The Compiler)"
__license__ = "GPL"
__maintainer__ = __author__
__email__ = "mail@qutebrowser.org"
__version_info__ = (1, 0, 2)
__version_info__ = (1, 0, 3)
__version__ = '.'.join(str(e) for e in __version_info__)
__description__ = "A keyboard-driven, vim-like browser based on PyQt5."

View File

@@ -1599,8 +1599,7 @@ class CommandDispatcher:
self.on_editing_finished, elem))
ed.edit(text)
@cmdutils.register(instance='command-dispatcher', hide=True,
scope='window')
@cmdutils.register(instance='command-dispatcher', scope='window')
def open_editor(self):
"""Open an external editor with the currently selected form field.

View File

@@ -29,7 +29,7 @@ def option(*, info):
model = completionmodel.CompletionModel(column_widths=(20, 70, 10))
options = ((opt.name, opt.description, info.config.get_str(opt.name))
for opt in configdata.DATA.values())
model.add_category(listcategory.ListCategory("Options", sorted(options)))
model.add_category(listcategory.ListCategory("Options", options))
return model
@@ -39,7 +39,7 @@ def customized_option(*, info):
options = ((opt.name, opt.description, info.config.get_str(opt.name))
for opt, _value in info.config)
model.add_category(listcategory.ListCategory("Customized options",
sorted(options)))
options))
return model
@@ -66,8 +66,7 @@ def value(optname, *_values, info):
vals = opt.typ.complete()
if vals is not None:
model.add_category(listcategory.ListCategory("Completions",
sorted(vals)))
model.add_category(listcategory.ListCategory("Completions", vals))
return model

View File

@@ -31,7 +31,7 @@ class ListCategory(QSortFilterProxyModel):
"""Expose a list of items as a category for the CompletionModel."""
def __init__(self, name, items, delete_func=None, parent=None):
def __init__(self, name, items, sort=True, delete_func=None, parent=None):
super().__init__(parent)
self.name = name
self.srcmodel = QStandardItemModel(parent=self)
@@ -43,6 +43,7 @@ class ListCategory(QSortFilterProxyModel):
self.srcmodel.appendRow([QStandardItem(x) for x in item])
self.setSourceModel(self.srcmodel)
self.delete_func = delete_func
self._sort = sort
def set_pattern(self, val):
"""Setter for pattern.
@@ -60,19 +61,33 @@ class ListCategory(QSortFilterProxyModel):
sortcol = 0
self.sort(sortcol)
def lessThan(self, _lindex, rindex):
def lessThan(self, lindex, rindex):
"""Custom sorting implementation.
Prefers all items which start with self._pattern. Other than that, keep
items in their original order.
Prefers all items which start with self._pattern. Other than that, uses
normal Python string sorting.
Args:
_lindex: The QModelIndex of the left item (*left* < right)
lindex: The QModelIndex of the left item (*left* < right)
rindex: The QModelIndex of the right item (left < *right*)
Return:
True if left < right, else False
"""
qtutils.ensure_valid(lindex)
qtutils.ensure_valid(rindex)
left = self.srcmodel.data(lindex)
right = self.srcmodel.data(rindex)
return not right.startswith(self._pattern)
leftstart = left.startswith(self._pattern)
rightstart = right.startswith(self._pattern)
if leftstart and not rightstart:
return True
elif rightstart and not leftstart:
return False
elif self._sort:
return left < right
else:
return False

View File

@@ -43,7 +43,7 @@ def helptopic(*, info):
for opt in configdata.DATA.values())
model.add_category(listcategory.ListCategory("Commands", cmdlist))
model.add_category(listcategory.ListCategory("Settings", sorted(settings)))
model.add_category(listcategory.ListCategory("Settings", settings))
return model
@@ -59,7 +59,8 @@ def quickmark(*, info=None): # pylint: disable=unused-argument
model = completionmodel.CompletionModel(column_widths=(30, 70, 0))
marks = objreg.get('quickmark-manager').marks.items()
model.add_category(listcategory.ListCategory('Quickmarks', marks,
delete_func=delete))
delete_func=delete,
sort=False))
return model
@@ -75,7 +76,8 @@ def bookmark(*, info=None): # pylint: disable=unused-argument
model = completionmodel.CompletionModel(column_widths=(30, 70, 0))
marks = objreg.get('bookmark-manager').marks.items()
model.add_category(listcategory.ListCategory('Bookmarks', marks,
delete_func=delete))
delete_func=delete,
sort=False))
return model

View File

@@ -61,9 +61,9 @@ def url(*, info):
bookmarks = objreg.get('bookmark-manager').marks.items()
model.add_category(listcategory.ListCategory(
'Quickmarks', quickmarks, delete_func=_delete_quickmark))
'Quickmarks', quickmarks, delete_func=_delete_quickmark, sort=False))
model.add_category(listcategory.ListCategory(
'Bookmarks', bookmarks, delete_func=_delete_bookmark))
'Bookmarks', bookmarks, delete_func=_delete_bookmark, sort=False))
if info.config.get('completion.web_history_max_items') != 0:
hist_cat = histcategory.HistoryCategory(delete_func=_delete_history)

View File

@@ -198,7 +198,7 @@ class KeyConfig:
bindings_commands = self._config.get_obj('bindings.commands')
if key in val.bindings.commands[mode]:
if val.bindings.commands[mode].get(key, None) is not None:
# In custom bindings -> remove it
del bindings_commands[mode][key]
elif key in val.bindings.default[mode]:

View File

@@ -755,6 +755,6 @@ def get_diff():
lexer = pygments.lexers.DiffLexer()
formatter = pygments.formatters.HtmlFormatter(
full=True, linenos='table',
title='Config diff')
title='Diffing pre-1.0 default config with pre-1.0 modified config')
# pylint: enable=no-member
return pygments.highlight(conf_diff + key_diff, lexer, formatter)

View File

@@ -249,13 +249,14 @@ class TabbedBrowser(tabwidget.TabWidget):
def tab_close_prompt_if_pinned(self, tab, force, yes_action):
"""Helper method for tab_close.
If tab is pinned, prompt. If everything is good, run yes_action.
If tab is pinned, prompt. If not, run yes_action.
If tab is destroyed, abort question.
"""
if tab.data.pinned and not force:
message.confirm_async(
title='Pinned Tab',
text="Are you sure you want to close a pinned tab?",
yes_action=yes_action, default=False)
yes_action=yes_action, default=False, abort_on=[tab.destroyed])
else:
yes_action()

View File

@@ -310,7 +310,7 @@ class TabBar(QTabBar):
return self.parent().currentWidget()
@pyqtSlot(str)
def _on_config_changed(self, option):
def _on_config_changed(self, option: str):
if option == 'fonts.tabs':
self._set_font()
elif option == 'tabs.favicons.scale':
@@ -325,6 +325,12 @@ class TabBar(QTabBar):
if option.startswith('colors.tabs.'):
self.update()
# Clear _minimum_tab_size_hint_helper cache when appropriate
if option in ["tabs.indicator_padding",
"tabs.padding",
"tabs.width.indicator"]:
self._minimum_tab_size_hint_helper.cache_clear()
def _on_show_switching_delay_changed(self):
"""Set timer interval when tabs.show_switching_delay got changed."""
self._auto_hide_timer.setInterval(config.val.tabs.show_switching_delay)
@@ -424,7 +430,7 @@ class TabBar(QTabBar):
return
super().mousePressEvent(e)
def minimumTabSizeHint(self, index, ellipsis: bool = True):
def minimumTabSizeHint(self, index, ellipsis: bool = True) -> QSize:
"""Set the minimum tab size to indicator/icon/... text.
Args:
@@ -434,38 +440,47 @@ class TabBar(QTabBar):
Return:
A QSize of the smallest tab size we can make.
"""
text = '\u2026' if ellipsis else self.tabText(index)
icon = self.tabIcon(index)
extent = self.style().pixelMetric(QStyle.PM_TabBarIconSize, None, self)
if icon.isNull():
icon_width = 0
else:
icon_width = icon.actualSize(QSize(extent, extent)).width()
return self._minimum_tab_size_hint_helper(self.tabText(index),
icon_width,
ellipsis)
@functools.lru_cache(maxsize=2**9)
def _minimum_tab_size_hint_helper(self, tab_text: str,
icon_width: int,
ellipsis: bool) -> QSize:
"""Helper function to cache tab results.
Config values accessed in here should be added to _on_config_changed to
ensure cache is flushed when needed.
"""
text = '\u2026' if ellipsis else tab_text
# Don't ever shorten if text is shorter than the ellipsis
text_width = min(self.fontMetrics().width(text),
self.fontMetrics().width(self.tabText(index)))
icon = self.tabIcon(index)
self.fontMetrics().width(tab_text))
padding = config.val.tabs.padding
indicator_padding = config.val.tabs.indicator_padding
padding_h = padding.left + padding.right
padding_h += indicator_padding.left + indicator_padding.right
padding_v = padding.top + padding.bottom
if icon.isNull():
icon_size = QSize(0, 0)
else:
extent = self.style().pixelMetric(QStyle.PM_TabBarIconSize, None,
self)
icon_size = icon.actualSize(QSize(extent, extent))
height = self.fontMetrics().height() + padding_v
width = (text_width + icon_size.width() +
width = (text_width + icon_width +
padding_h + config.val.tabs.width.indicator)
return QSize(width, height)
def _tab_total_width_pinned(self):
"""Get the current total width of pinned tabs.
This width is calculated assuming no shortening due to ellipsis."""
return sum(self.minimumTabSizeHint(idx, ellipsis=False).width()
for idx in range(self.count())
if self._tab_pinned(idx))
def _pinnedCount(self) -> int:
"""Get the number of pinned tabs."""
return sum(self._tab_pinned(idx) for idx in range(self.count()))
def _pinned_statistics(self) -> (int, int):
"""Get the number of pinned tabs and the total width of pinned tabs."""
pinned_list = [idx for idx in range(self.count())
if self._tab_pinned(idx)]
pinned_count = len(pinned_list)
pinned_width = sum(self.minimumTabSizeHint(idx, ellipsis=False).width()
for idx in pinned_list)
return (pinned_count, pinned_width)
def _tab_pinned(self, index: int) -> bool:
"""Return True if tab is pinned."""
@@ -504,8 +519,8 @@ class TabBar(QTabBar):
return QSize()
else:
pinned = self._tab_pinned(index)
no_pinned_count = self.count() - self._pinnedCount()
pinned_width = self._tab_total_width_pinned()
pinned_count, pinned_width = self._pinned_statistics()
no_pinned_count = self.count() - pinned_count
no_pinned_width = self.width() - pinned_width
if pinned:

View File

@@ -68,9 +68,9 @@ class SqliteError(SqlError):
# https://github.com/qutebrowser/qutebrowser/issues/2930
# https://github.com/qutebrowser/qutebrowser/issues/3004
environmental_errors = [
'5', # SQLITE_BUSY ("database is locked")
'8', # SQLITE_READONLY
'9', # SQLITE_LOCKED,
'13', # SQLITE_FULL,
'13', # SQLITE_FULL
]
self.environmental = error.nativeErrorCode() in environmental_errors

View File

@@ -171,6 +171,7 @@ def debug_cache_stats():
prefix_info = configdata.is_valid_prefix.cache_info()
# pylint: disable=protected-access
render_stylesheet_info = config._render_stylesheet.cache_info()
# pylint: enable=protected-access
history_info = None
try:
@@ -181,9 +182,17 @@ def debug_cache_stats():
except ImportError:
pass
tabbed_browser = objreg.get('tabbed-browser', scope='window',
window='last-focused')
# pylint: disable=protected-access
tab_bar = tabbed_browser.tabBar()
tabbed_browser_info = tab_bar._minimum_tab_size_hint_helper.cache_info()
# pylint: enable=protected-access
log.misc.debug('is_valid_prefix: {}'.format(prefix_info))
log.misc.debug('_render_stylesheet: {}'.format(render_stylesheet_info))
log.misc.debug('history: {}'.format(history_info))
log.misc.debug('tab width cache: {}'.format(tabbed_browser_info))
@cmdutils.register(debug=True)

View File

@@ -147,7 +147,7 @@ def test_remove_rows(hist, model_validator):
cat.set_pattern('')
hist.delete('url', 'foo')
cat.removeRows(0, 1)
model_validator.validate([('bar', 'Bar', '1970-01-01')])
model_validator.validate([('bar', 'Bar')])
def test_remove_rows_fetch(hist):

View File

@@ -24,27 +24,36 @@ import pytest
from qutebrowser.completion.models import listcategory
@pytest.mark.parametrize('pattern, before, after', [
@pytest.mark.parametrize('pattern, before, after, after_nosort', [
('foo',
[('foo', ''), ('bar', '')],
[('foo', '')],
[('foo', '')]),
('foo',
[('foob', ''), ('fooc', ''), ('fooa', '')],
[('fooa', ''), ('foob', ''), ('fooc', '')],
[('foob', ''), ('fooc', ''), ('fooa', '')]),
# prefer foobar as it starts with the pattern
('foo',
[('barfoo', ''), ('foobar', '')],
[('foobar', ''), ('barfoo', '')]),
[('barfoo', ''), ('foobaz', ''), ('foobar', '')],
[('foobar', ''), ('foobaz', ''), ('barfoo', '')],
[('foobaz', ''), ('foobar', ''), ('barfoo', '')]),
('foo',
[('foo', 'bar'), ('bar', 'foo'), ('bar', 'bar')],
[('foo', 'bar'), ('bar', 'foo')],
[('foo', 'bar'), ('bar', 'foo')]),
])
def test_set_pattern(pattern, before, after, model_validator):
def test_set_pattern(pattern, before, after, after_nosort, model_validator):
"""Validate the filtering and sorting results of set_pattern."""
cat = listcategory.ListCategory('Foo', before)
model_validator.set_model(cat)
cat.set_pattern(pattern)
model_validator.validate(after)
cat = listcategory.ListCategory('Foo', before, sort=False)
model_validator.set_model(cat)
cat.set_pattern(pattern)
model_validator.validate(after_nosort)

View File

@@ -466,9 +466,9 @@ def test_session_completion(qtmodeltester, session_manager_stub):
qtmodeltester.check(model)
_check_completions(model, {
"Sessions": [('default', None, None),
('1', None, None),
('2', None, None)]
"Sessions": [('1', None, None),
('2', None, None),
('default', None, None)]
})

View File

@@ -269,6 +269,22 @@ class TestKeyConfig:
match="Can't find binding 'foobar' in normal mode"):
key_config_stub.unbind('foobar', mode='normal')
def test_unbound_twice(self, key_config_stub, config_stub, no_bindings):
"""Try unbinding an already-unbound default key.
For custom-bound keys (in bindings.commands), it's okay to display an
error, as this isn't something you'd do in e.g a config.py anyways.
https://github.com/qutebrowser/qutebrowser/issues/3162
"""
config_stub.val.bindings.default = {'normal': {'a': 'nop'}}
config_stub.val.bindings.commands = no_bindings
key_config_stub.unbind('a')
assert key_config_stub.get_command('a', mode='normal') is None
key_config_stub.unbind('a')
assert key_config_stub.get_command('a', mode='normal') is None
class TestConfig:

View File

@@ -52,3 +52,16 @@ class TestTabWidget:
with qtbot.waitExposed(widget):
widget.show()
def test_update_tab_titles_benchmark(self, benchmark, widget,
qtbot, fake_web_tab):
"""Benchmark for update_tab_titles."""
widget.addTab(fake_web_tab(), 'foobar')
widget.addTab(fake_web_tab(), 'foobar2')
widget.addTab(fake_web_tab(), 'foobar3')
widget.addTab(fake_web_tab(), 'foobar4')
with qtbot.waitExposed(widget):
widget.show()
benchmark(widget._update_tab_titles)

View File

@@ -39,7 +39,7 @@ def test_sqlerror():
class TestSqliteError:
@pytest.mark.parametrize('error_code, environmental', [
('9', True), # SQLITE_LOCKED
('5', True), # SQLITE_BUSY
('19', False), # SQLITE_CONSTRAINT
])
def test_environmental(self, error_code, environmental):