Merge branch 'master' into feature/directory-browser
This commit is contained in:
commit
77190554cc
@ -12,9 +12,8 @@ install:
|
||||
- C:\Python27\python -u scripts\dev\ci_install.py
|
||||
|
||||
test_script:
|
||||
- C:\Python34\Scripts\tox -e smoke
|
||||
- C:\Python34\Scripts\tox -e smoke-frozen
|
||||
- C:\Python34\Scripts\tox -e unittests
|
||||
- C:\Python34\Scripts\tox -e py34
|
||||
- C:\Python34\Scripts\tox -e unittests-frozen
|
||||
- C:\Python34\Scripts\tox -e smoke-frozen
|
||||
- C:\Python34\Scripts\tox -e pyflakes
|
||||
- C:\Python34\Scripts\tox -e pylint
|
||||
|
@ -12,3 +12,6 @@ exclude_lines =
|
||||
raise AssertionError
|
||||
raise NotImplementedError
|
||||
if __name__ == ["']__main__["']:
|
||||
|
||||
[xml]
|
||||
output=.coverage.xml
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -21,7 +21,7 @@ __pycache__
|
||||
/.venv
|
||||
/.coverage
|
||||
/htmlcov
|
||||
/coverage.xml
|
||||
/.coverage.xml
|
||||
/.tox
|
||||
/testresults.html
|
||||
/.cache
|
||||
|
@ -17,7 +17,7 @@ install:
|
||||
- python scripts/dev/ci_install.py
|
||||
|
||||
script:
|
||||
- xvfb-run -s "-screen 0 640x480x16" tox -e unittests,smoke
|
||||
- xvfb-run -s "-screen 0 640x480x16" tox -e py34
|
||||
- tox -e misc
|
||||
- tox -e pep257
|
||||
- tox -e pyflakes
|
||||
|
@ -48,6 +48,8 @@ Changed
|
||||
mode and is not hidden anymore.
|
||||
- `minimal_webkit_testbrowser.py` now has a `--webengine` switch to test
|
||||
QtWebEngine if it's installed.
|
||||
- The column width percentages for the completion view now depend on the
|
||||
completion model.
|
||||
|
||||
Fixed
|
||||
~~~~~
|
||||
@ -60,6 +62,8 @@ Fixed
|
||||
- Fixed entering of insert mode when certain disabled text fields were clicked.
|
||||
- Fixed a crash when using `:set` with `-p` and `!` (invert value)
|
||||
- Downloads with unknown size are now handled correctly.
|
||||
- `:navigate increment/decrement` (`<Ctrl-A>`/`<Ctrl-X>`) now handles some
|
||||
corner-cases better.
|
||||
|
||||
Removed
|
||||
~~~~~~~
|
||||
|
@ -143,11 +143,12 @@ Contributors, sorted by the number of commits in descending order:
|
||||
* Lamar Pavel
|
||||
* Austin Anderson
|
||||
* Artur Shaik
|
||||
* Alexander Cogneau
|
||||
* ZDarian
|
||||
* Peter Vilim
|
||||
* John ShaggyTwoDope Jenkins
|
||||
* Daniel
|
||||
* Jimmy
|
||||
* Alexander Cogneau
|
||||
* Zach-Button
|
||||
* rikn00
|
||||
* Patric Schmitz
|
||||
@ -157,6 +158,7 @@ Contributors, sorted by the number of commits in descending order:
|
||||
* sbinix
|
||||
* Tobias Patzl
|
||||
* Johannes Altmanninger
|
||||
* Thorsten Wißmann
|
||||
* Samir Benmendil
|
||||
* Regina Hug
|
||||
* Mathias Fussenegger
|
||||
@ -166,7 +168,6 @@ Contributors, sorted by the number of commits in descending order:
|
||||
* zwarag
|
||||
* error800
|
||||
* Tim Harder
|
||||
* Thorsten Wißmann
|
||||
* Thiago Barroso Perrotta
|
||||
* Matthias Lisin
|
||||
* Helen Sherwood-Taylor
|
||||
|
@ -630,6 +630,8 @@ Syntax: +:tab-focus ['index']+
|
||||
|
||||
Select the tab given as argument/[count].
|
||||
|
||||
If neither count nor index are given, it behaves like tab-next.
|
||||
|
||||
==== positional arguments
|
||||
* +'index'+: The tab index to focus, starting with 1. The special value `last` focuses the last focused tab.
|
||||
|
||||
|
@ -8,6 +8,7 @@ markers =
|
||||
osx: Tests which only can run on OS X.
|
||||
not_frozen: Tests which can't be run if sys.frozen is True.
|
||||
frozen: Tests which can only be run if sys.frozen is True.
|
||||
integration: Tests which test a bigger portion of code, run without coverage.
|
||||
flakes-ignore =
|
||||
UnusedImport
|
||||
UnusedVariable
|
||||
|
@ -272,7 +272,7 @@ def process_pos_args(args, via_ipc=False, cwd=None):
|
||||
log.init.debug("Startup URL {}".format(cmd))
|
||||
try:
|
||||
url = urlutils.fuzzy_url(cmd, cwd, relative=True)
|
||||
except urlutils.FuzzyUrlError as e:
|
||||
except urlutils.InvalidUrlError as e:
|
||||
message.error('current', "Error in startup argument '{}': "
|
||||
"{}".format(cmd, e))
|
||||
else:
|
||||
@ -302,7 +302,7 @@ def _open_startpage(win_id=None):
|
||||
for urlstr in config.get('general', 'startpage'):
|
||||
try:
|
||||
url = urlutils.fuzzy_url(urlstr, do_search=False)
|
||||
except urlutils.FuzzyUrlError as e:
|
||||
except urlutils.InvalidUrlError as e:
|
||||
message.error('current', "Error when opening startpage: "
|
||||
"{}".format(e))
|
||||
tabbed_browser.tabopen(QUrl('about:blank'))
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
"""Command dispatcher for TabbedBrowser."""
|
||||
|
||||
import re
|
||||
import os
|
||||
import shlex
|
||||
import posixpath
|
||||
@ -304,7 +303,7 @@ class CommandDispatcher:
|
||||
else:
|
||||
try:
|
||||
url = urlutils.fuzzy_url(url)
|
||||
except urlutils.FuzzyUrlError as e:
|
||||
except urlutils.InvalidUrlError as e:
|
||||
raise cmdexc.CommandError(e)
|
||||
if tab or bg or window:
|
||||
self._open(url, tab, bg, window)
|
||||
@ -472,29 +471,10 @@ class CommandDispatcher:
|
||||
background: Open the link in a new background tab.
|
||||
window: Open the link in a new window.
|
||||
"""
|
||||
encoded = bytes(url.toEncoded()).decode('ascii')
|
||||
# Get the last number in a string
|
||||
match = re.match(r'(.*\D|^)(\d+)(.*)', encoded)
|
||||
if not match:
|
||||
raise cmdexc.CommandError("No number found in URL!")
|
||||
pre, number, post = match.groups()
|
||||
if not number:
|
||||
raise cmdexc.CommandError("No number found in URL!")
|
||||
try:
|
||||
val = int(number)
|
||||
except ValueError:
|
||||
raise cmdexc.CommandError("Could not parse number '{}'.".format(
|
||||
number))
|
||||
if incdec == 'decrement':
|
||||
if val <= 0:
|
||||
raise cmdexc.CommandError("Can't decrement {}!".format(val))
|
||||
val -= 1
|
||||
elif incdec == 'increment':
|
||||
val += 1
|
||||
else:
|
||||
raise ValueError("Invalid value {} for indec!".format(incdec))
|
||||
urlstr = ''.join([pre, str(val), post]).encode('ascii')
|
||||
new_url = QUrl.fromEncoded(urlstr)
|
||||
new_url = urlutils.incdec_number(url, incdec)
|
||||
except urlutils.IncDecError as error:
|
||||
raise cmdexc.CommandError(error.msg)
|
||||
self._open(new_url, tab, background, window)
|
||||
|
||||
def _navigate_up(self, url, tab, background, window):
|
||||
@ -889,7 +869,7 @@ class CommandDispatcher:
|
||||
log.misc.debug("{} contained: '{}'".format(target, text))
|
||||
try:
|
||||
url = urlutils.fuzzy_url(text)
|
||||
except urlutils.FuzzyUrlError as e:
|
||||
except urlutils.InvalidUrlError as e:
|
||||
raise cmdexc.CommandError(e)
|
||||
self._open(url, tab, bg, window)
|
||||
|
||||
@ -898,6 +878,8 @@ class CommandDispatcher:
|
||||
def tab_focus(self, index: {'type': (int, 'last')}=None, count=None):
|
||||
"""Select the tab given as argument/[count].
|
||||
|
||||
If neither count nor index are given, it behaves like tab-next.
|
||||
|
||||
Args:
|
||||
index: The tab index to focus, starting with 1. The special value
|
||||
`last` focuses the last focused tab.
|
||||
@ -906,6 +888,9 @@ class CommandDispatcher:
|
||||
if index == 'last':
|
||||
self._tab_focus_last()
|
||||
return
|
||||
if index is None and count is None:
|
||||
self.tab_next()
|
||||
return
|
||||
try:
|
||||
idx = cmdutils.arg_or_count(index, count, default=1,
|
||||
countzero=self._count())
|
||||
@ -1083,7 +1068,7 @@ class CommandDispatcher:
|
||||
"""
|
||||
try:
|
||||
url = urlutils.fuzzy_url(url)
|
||||
except urlutils.FuzzyUrlError as e:
|
||||
except urlutils.InvalidUrlError as e:
|
||||
raise cmdexc.CommandError(e)
|
||||
self._open(url, tab, bg, window)
|
||||
|
||||
|
@ -350,16 +350,20 @@ class NetworkManager(QNetworkAccessManager):
|
||||
current_url = webview.url()
|
||||
referer_header_conf = config.get('network', 'referer-header')
|
||||
|
||||
if referer_header_conf == 'never':
|
||||
# Note: using ''.encode('ascii') sends a header with no value,
|
||||
# instead of no header at all
|
||||
req.setRawHeader('Referer'.encode('ascii'), QByteArray())
|
||||
elif (referer_header_conf == 'same-domain' and
|
||||
current_url.isValid() and
|
||||
not urlutils.same_domain(req.url(), current_url)):
|
||||
req.setRawHeader('Referer'.encode('ascii'), QByteArray())
|
||||
# If refer_header_conf is set to 'always', we leave the header alone as
|
||||
# QtWebKit did set it.
|
||||
try:
|
||||
if referer_header_conf == 'never':
|
||||
# Note: using ''.encode('ascii') sends a header with no value,
|
||||
# instead of no header at all
|
||||
req.setRawHeader('Referer'.encode('ascii'), QByteArray())
|
||||
elif (referer_header_conf == 'same-domain' and
|
||||
not urlutils.same_domain(req.url(), current_url)):
|
||||
req.setRawHeader('Referer'.encode('ascii'), QByteArray())
|
||||
# If refer_header_conf is set to 'always', we leave the header
|
||||
# alone as QtWebKit did set it.
|
||||
except urlutils.InvalidUrlError:
|
||||
# req.url() or current_url can be invalid - this happens on
|
||||
# https://www.playstation.com/ for example.
|
||||
pass
|
||||
|
||||
accept_language = config.get('network', 'accept-language')
|
||||
if accept_language is not None:
|
||||
|
@ -225,7 +225,7 @@ class QuickmarkManager(UrlMarkManager):
|
||||
urlstr = self.marks[name]
|
||||
try:
|
||||
url = urlutils.fuzzy_url(urlstr, do_search=False)
|
||||
except urlutils.FuzzyUrlError as e:
|
||||
except urlutils.InvalidUrlError as e:
|
||||
raise InvalidUrlError(
|
||||
"Invalid URL for quickmark {}: {}".format(name, str(e)))
|
||||
return url
|
||||
|
@ -339,8 +339,6 @@ def get_child_frames(startframe):
|
||||
def focus_elem(frame):
|
||||
"""Get the focused element in a web frame.
|
||||
|
||||
FIXME: Add tests.
|
||||
|
||||
Args:
|
||||
frame: The QWebFrame to search in.
|
||||
"""
|
||||
|
@ -28,6 +28,7 @@ from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QItemSelectionModel
|
||||
|
||||
from qutebrowser.config import config, style
|
||||
from qutebrowser.completion import completiondelegate, completer
|
||||
from qutebrowser.completion.models import base
|
||||
from qutebrowser.utils import qtutils, objreg, utils
|
||||
|
||||
|
||||
@ -38,15 +39,13 @@ class CompletionView(QTreeView):
|
||||
Based on QTreeView but heavily customized so root elements show as category
|
||||
headers, and children show as flat list.
|
||||
|
||||
Class attributes:
|
||||
COLUMN_WIDTHS: A list of column widths, in percent.
|
||||
|
||||
Attributes:
|
||||
enabled: Whether showing the CompletionView is enabled.
|
||||
_win_id: The ID of the window this CompletionView is associated with.
|
||||
_height: The height to use for the CompletionView.
|
||||
_height_perc: Either None or a percentage if height should be relative.
|
||||
_delegate: The item delegate used.
|
||||
_column_widths: A list of column widths, in percent.
|
||||
|
||||
Signals:
|
||||
resize_completion: Emitted when the completion should be resized.
|
||||
@ -82,7 +81,6 @@ class CompletionView(QTreeView):
|
||||
border: 0px;
|
||||
}
|
||||
"""
|
||||
COLUMN_WIDTHS = (20, 70, 10)
|
||||
|
||||
# FIXME style scrollbar
|
||||
# https://github.com/The-Compiler/qutebrowser/issues/117
|
||||
@ -103,6 +101,8 @@ class CompletionView(QTreeView):
|
||||
# FIXME handle new aliases.
|
||||
# objreg.get('config').changed.connect(self.init_command_completion)
|
||||
|
||||
self._column_widths = base.BaseCompletionModel.COLUMN_WIDTHS
|
||||
|
||||
self._delegate = completiondelegate.CompletionItemDelegate(self)
|
||||
self.setItemDelegate(self._delegate)
|
||||
style.set_register_stylesheet(self)
|
||||
@ -128,9 +128,9 @@ class CompletionView(QTreeView):
|
||||
return utils.get_repr(self)
|
||||
|
||||
def _resize_columns(self):
|
||||
"""Resize the completion columns based on COLUMN_WIDTHS."""
|
||||
"""Resize the completion columns based on column_widths."""
|
||||
width = self.size().width()
|
||||
pixel_widths = [(width * perc // 100) for perc in self.COLUMN_WIDTHS]
|
||||
pixel_widths = [(width * perc // 100) for perc in self._column_widths]
|
||||
if self.verticalScrollBar().isVisible():
|
||||
pixel_widths[-1] -= self.style().pixelMetric(
|
||||
QStyle.PM_ScrollBarExtent) + 5
|
||||
@ -203,6 +203,8 @@ class CompletionView(QTreeView):
|
||||
sel_model.deleteLater()
|
||||
for i in range(model.rowCount()):
|
||||
self.expand(model.index(i, 0))
|
||||
|
||||
self._column_widths = model.srcmodel.COLUMN_WIDTHS
|
||||
self._resize_columns()
|
||||
self.maybe_resize_completion()
|
||||
|
||||
|
@ -39,8 +39,14 @@ class BaseCompletionModel(QStandardItemModel):
|
||||
|
||||
Used for showing completions later in the CompletionView. Supports setting
|
||||
marks and adding new categories/items easily.
|
||||
|
||||
Class Attributes:
|
||||
COLUMN_WIDTHS: The width percentages of the columns used in the
|
||||
completion view.
|
||||
"""
|
||||
|
||||
COLUMN_WIDTHS = (30, 70, 0)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
self.setColumnCount(3)
|
||||
|
@ -32,6 +32,8 @@ class SettingSectionCompletionModel(base.BaseCompletionModel):
|
||||
|
||||
# pylint: disable=abstract-method
|
||||
|
||||
COLUMN_WIDTHS = (20, 70, 10)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
cat = self.new_category("Sections")
|
||||
@ -51,6 +53,8 @@ class SettingOptionCompletionModel(base.BaseCompletionModel):
|
||||
|
||||
# pylint: disable=abstract-method
|
||||
|
||||
COLUMN_WIDTHS = (20, 70, 10)
|
||||
|
||||
def __init__(self, section, parent=None):
|
||||
super().__init__(parent)
|
||||
cat = self.new_category(section)
|
||||
@ -104,6 +108,8 @@ class SettingValueCompletionModel(base.BaseCompletionModel):
|
||||
|
||||
# pylint: disable=abstract-method
|
||||
|
||||
COLUMN_WIDTHS = (20, 70, 10)
|
||||
|
||||
def __init__(self, section, option, parent=None):
|
||||
super().__init__(parent)
|
||||
self._section = section
|
||||
|
@ -40,6 +40,8 @@ class UrlCompletionModel(base.BaseCompletionModel):
|
||||
TEXT_COLUMN = 1
|
||||
TIME_COLUMN = 2
|
||||
|
||||
COLUMN_WIDTHS = (40, 50, 10)
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
|
@ -1236,7 +1236,7 @@ KEY_DATA = collections.OrderedDict([
|
||||
('tab-move', ['gm']),
|
||||
('tab-move -', ['gl']),
|
||||
('tab-move +', ['gr']),
|
||||
('tab-next', ['J', 'gt']),
|
||||
('tab-focus', ['J', 'gt']),
|
||||
('tab-prev', ['K', 'gT']),
|
||||
('tab-clone', ['gC']),
|
||||
('reload', ['r']),
|
||||
|
@ -803,8 +803,8 @@ class File(BaseType):
|
||||
value = os.path.expandvars(value)
|
||||
if not os.path.isabs(value):
|
||||
cfgdir = standarddir.config()
|
||||
if cfgdir is not None:
|
||||
return os.path.join(cfgdir, value)
|
||||
assert cfgdir is not None
|
||||
return os.path.join(cfgdir, value)
|
||||
return value
|
||||
|
||||
def validate(self, value):
|
||||
@ -1113,7 +1113,7 @@ class FuzzyUrl(BaseType):
|
||||
from qutebrowser.utils import urlutils
|
||||
try:
|
||||
self.transform(value)
|
||||
except urlutils.FuzzyUrlError as e:
|
||||
except urlutils.InvalidUrlError as e:
|
||||
raise configexc.ValidationError(value, str(e))
|
||||
|
||||
def transform(self, value):
|
||||
|
@ -26,7 +26,7 @@ class TextWrapper(textwrap.TextWrapper):
|
||||
|
||||
"""Text wrapper customized to be used in configs."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
def __init__(self, **kwargs):
|
||||
kw = {
|
||||
'width': 72,
|
||||
'replace_whitespace': False,
|
||||
@ -36,4 +36,4 @@ class TextWrapper(textwrap.TextWrapper):
|
||||
'subsequent_indent': '# ',
|
||||
}
|
||||
kw.update(kwargs)
|
||||
super().__init__(*args, **kw)
|
||||
super().__init__(**kw)
|
||||
|
@ -55,9 +55,11 @@ class TextBase(QLabel):
|
||||
Args:
|
||||
width: The maximal width the text should take.
|
||||
"""
|
||||
if self.text is not None:
|
||||
if self.text():
|
||||
self._elided_text = self.fontMetrics().elidedText(
|
||||
self.text(), self._elidemode, width, Qt.TextShowMnemonic)
|
||||
else:
|
||||
self._elided_text = ''
|
||||
|
||||
def setText(self, txt):
|
||||
"""Extend QLabel::setText.
|
||||
|
@ -25,7 +25,7 @@ import functools
|
||||
import datetime
|
||||
import contextlib
|
||||
|
||||
from PyQt5.QtCore import QEvent, QMetaMethod, QObject
|
||||
from PyQt5.QtCore import Qt, QEvent, QMetaMethod, QObject
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from qutebrowser.utils import log, utils, qtutils, objreg
|
||||
@ -56,7 +56,7 @@ def log_signals(obj):
|
||||
dbg = dbg_signal(signal, args)
|
||||
try:
|
||||
r = repr(obj)
|
||||
except RuntimeError:
|
||||
except RuntimeError: # pragma: no cover
|
||||
r = '<deleted>'
|
||||
log.signals.debug("Signal in {}: {}".format(r, dbg))
|
||||
|
||||
@ -68,8 +68,9 @@ def log_signals(obj):
|
||||
qtutils.ensure_valid(meta_method)
|
||||
if meta_method.methodType() == QMetaMethod.Signal:
|
||||
name = bytes(meta_method.name()).decode('ascii')
|
||||
signal = getattr(obj, name)
|
||||
signal.connect(functools.partial(log_slot, obj, signal))
|
||||
if name != 'destroyed':
|
||||
signal = getattr(obj, name)
|
||||
signal.connect(functools.partial(log_slot, obj, signal))
|
||||
|
||||
if inspect.isclass(obj):
|
||||
old_init = obj.__init__
|
||||
@ -105,19 +106,21 @@ def qenum_key(base, value, add_base=False, klass=None):
|
||||
klass = value.__class__
|
||||
if klass == int:
|
||||
raise TypeError("Can't guess enum class of an int!")
|
||||
|
||||
try:
|
||||
idx = klass.staticMetaObject.indexOfEnumerator(klass.__name__)
|
||||
idx = base.staticMetaObject.indexOfEnumerator(klass.__name__)
|
||||
ret = base.staticMetaObject.enumerator(idx).valueToKey(value)
|
||||
except AttributeError:
|
||||
idx = -1
|
||||
if idx != -1:
|
||||
ret = klass.staticMetaObject.enumerator(idx).valueToKey(value)
|
||||
else:
|
||||
ret = None
|
||||
|
||||
if ret is None:
|
||||
for name, obj in vars(base).items():
|
||||
if isinstance(obj, klass) and obj == value:
|
||||
ret = name
|
||||
break
|
||||
else:
|
||||
ret = '0x{:04x}'.format(int(value))
|
||||
|
||||
if add_base and hasattr(base, '__name__'):
|
||||
return '.'.join([base.__name__, ret])
|
||||
else:
|
||||
@ -177,7 +180,7 @@ def signal_name(sig):
|
||||
return m.group(1)
|
||||
|
||||
|
||||
def _format_args(args=None, kwargs=None):
|
||||
def format_args(args=None, kwargs=None):
|
||||
"""Format a list of arguments/kwargs to a function-call like string."""
|
||||
if args is not None:
|
||||
arglist = [utils.compact_text(repr(arg), 200) for arg in args]
|
||||
@ -199,7 +202,7 @@ def dbg_signal(sig, args):
|
||||
Return:
|
||||
A human-readable string representation of signal/args.
|
||||
"""
|
||||
return '{}({})'.format(signal_name(sig), _format_args(args))
|
||||
return '{}({})'.format(signal_name(sig), format_args(args))
|
||||
|
||||
|
||||
def format_call(func, args=None, kwargs=None, full=True):
|
||||
@ -218,7 +221,7 @@ def format_call(func, args=None, kwargs=None, full=True):
|
||||
name = utils.qualname(func)
|
||||
else:
|
||||
name = func.__name__
|
||||
return '{}({})'.format(name, _format_args(args, kwargs))
|
||||
return '{}({})'.format(name, format_args(args, kwargs))
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
@ -247,25 +250,30 @@ def _get_widgets():
|
||||
|
||||
def _get_pyqt_objects(lines, obj, depth=0):
|
||||
"""Recursive method for get_all_objects to get Qt objects."""
|
||||
for kid in obj.findChildren(QObject):
|
||||
for kid in obj.findChildren(QObject, '', Qt.FindDirectChildrenOnly):
|
||||
lines.append(' ' * depth + repr(kid))
|
||||
_get_pyqt_objects(lines, kid, depth + 1)
|
||||
|
||||
|
||||
def get_all_objects():
|
||||
def get_all_objects(start_obj=None):
|
||||
"""Get all children of an object recursively as a string."""
|
||||
output = ['']
|
||||
widget_lines = _get_widgets()
|
||||
widget_lines = [' ' + e for e in widget_lines]
|
||||
widget_lines.insert(0, "Qt widgets - {} objects".format(
|
||||
widget_lines.insert(0, "Qt widgets - {} objects:".format(
|
||||
len(widget_lines)))
|
||||
output += widget_lines
|
||||
|
||||
if start_obj is None:
|
||||
start_obj = QApplication.instance()
|
||||
|
||||
pyqt_lines = []
|
||||
_get_pyqt_objects(pyqt_lines, QApplication.instance())
|
||||
_get_pyqt_objects(pyqt_lines, start_obj)
|
||||
pyqt_lines = [' ' + e for e in pyqt_lines]
|
||||
pyqt_lines.insert(0, 'Qt objects - {} objects:'.format(
|
||||
len(pyqt_lines)))
|
||||
output += pyqt_lines
|
||||
|
||||
output += ['']
|
||||
output += pyqt_lines
|
||||
output += objreg.dump_objects()
|
||||
return '\n'.join(output)
|
||||
|
@ -37,6 +37,22 @@ from qutebrowser.commands import cmdexc
|
||||
# https://github.com/The-Compiler/qutebrowser/issues/108
|
||||
|
||||
|
||||
class InvalidUrlError(ValueError):
|
||||
|
||||
"""Error raised if a function got an invalid URL.
|
||||
|
||||
Inherits ValueError because that was the exception originally used for
|
||||
that, so there still might be some code around which checks for that.
|
||||
"""
|
||||
|
||||
def __init__(self, url):
|
||||
if url.isValid():
|
||||
raise ValueError("Got valid URL {}!".format(url.toDisplayString()))
|
||||
self.url = url
|
||||
self.msg = get_errstring(url)
|
||||
super().__init__(self.msg)
|
||||
|
||||
|
||||
def _parse_search_term(s):
|
||||
"""Get a search engine name and search term from a string.
|
||||
|
||||
@ -185,7 +201,7 @@ def fuzzy_url(urlstr, cwd=None, relative=False, do_search=True):
|
||||
qtutils.ensure_valid(url)
|
||||
else:
|
||||
if not url.isValid():
|
||||
raise FuzzyUrlError("Invalid URL '{}'!".format(urlstr), url)
|
||||
raise InvalidUrlError(url)
|
||||
return url
|
||||
|
||||
|
||||
@ -355,7 +371,7 @@ def host_tuple(url):
|
||||
This is suitable to identify a connection, e.g. for SSL errors.
|
||||
"""
|
||||
if not url.isValid():
|
||||
raise ValueError(get_errstring(url))
|
||||
raise InvalidUrlError(url)
|
||||
scheme, host, port = url.scheme(), url.host(), url.port()
|
||||
assert scheme
|
||||
if not host:
|
||||
@ -405,9 +421,9 @@ def same_domain(url1, url2):
|
||||
True if the domains are the same, False otherwise.
|
||||
"""
|
||||
if not url1.isValid():
|
||||
raise ValueError(get_errstring(url1))
|
||||
raise InvalidUrlError(url1)
|
||||
if not url2.isValid():
|
||||
raise ValueError(get_errstring(url2))
|
||||
raise InvalidUrlError(url2)
|
||||
|
||||
suffix1 = url1.topLevelDomain()
|
||||
suffix2 = url2.topLevelDomain()
|
||||
@ -422,24 +438,57 @@ def same_domain(url1, url2):
|
||||
return domain1 == domain2
|
||||
|
||||
|
||||
class FuzzyUrlError(Exception):
|
||||
class IncDecError(Exception):
|
||||
|
||||
"""Exception raised by fuzzy_url on problems.
|
||||
"""Exception raised by incdec_number on problems.
|
||||
|
||||
Attributes:
|
||||
msg: The error message to use.
|
||||
msg: The error message.
|
||||
url: The QUrl which caused the error.
|
||||
"""
|
||||
|
||||
def __init__(self, msg, url=None):
|
||||
def __init__(self, msg, url):
|
||||
super().__init__(msg)
|
||||
if url is not None and url.isValid():
|
||||
raise ValueError("Got valid URL {}!".format(url.toDisplayString()))
|
||||
self.url = url
|
||||
self.msg = msg
|
||||
|
||||
def __str__(self):
|
||||
if self.url is None or not self.url.errorString():
|
||||
return self.msg
|
||||
else:
|
||||
return '{}: {}'.format(self.msg, self.url.errorString())
|
||||
return '{}: {}'.format(self.msg, self.url.toString())
|
||||
|
||||
|
||||
def incdec_number(url, incdec):
|
||||
"""Find a number in the url and increment or decrement it.
|
||||
|
||||
Args:
|
||||
url: The current url
|
||||
incdec: Either 'increment' or 'decrement'
|
||||
|
||||
Return:
|
||||
The new url with the number incremented/decremented.
|
||||
|
||||
Raises IncDecError if the url contains no number.
|
||||
"""
|
||||
if not url.isValid():
|
||||
raise InvalidUrlError(url)
|
||||
|
||||
path = url.path()
|
||||
# Get the last number in a string
|
||||
match = re.match(r'(.*\D|^)(\d+)(.*)', path)
|
||||
if not match:
|
||||
raise IncDecError("No number found in URL!", url)
|
||||
pre, number, post = match.groups()
|
||||
# This should always succeed because we match \d+
|
||||
val = int(number)
|
||||
if incdec == 'decrement':
|
||||
if val <= 0:
|
||||
raise IncDecError("Can't decrement {}!".format(val), url)
|
||||
val -= 1
|
||||
elif incdec == 'increment':
|
||||
val += 1
|
||||
else:
|
||||
raise ValueError("Invalid value {} for indec!".format(incdec))
|
||||
new_path = ''.join([pre, str(val), post])
|
||||
# Make a copy of the QUrl so we don't modify the original
|
||||
new_url = QUrl(url)
|
||||
new_url.setPath(new_path)
|
||||
return new_url
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
"""Enforce perfect coverage on some files."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import os.path
|
||||
|
||||
@ -37,6 +38,8 @@ PERFECT_FILES = [
|
||||
'qutebrowser/browser/tabhistory.py',
|
||||
'qutebrowser/browser/http.py',
|
||||
'qutebrowser/browser/rfc6266.py',
|
||||
'qutebrowser/browser/webelem.py',
|
||||
'qutebrowser/browser/network/schemehandler.py',
|
||||
|
||||
'qutebrowser/misc/readline.py',
|
||||
'qutebrowser/misc/split.py',
|
||||
@ -45,10 +48,12 @@ PERFECT_FILES = [
|
||||
'qutebrowser/mainwindow/statusbar/percentage.py',
|
||||
'qutebrowser/mainwindow/statusbar/progress.py',
|
||||
'qutebrowser/mainwindow/statusbar/tabindex.py',
|
||||
'qutebrowser/mainwindow/statusbar/textbase.py',
|
||||
|
||||
'qutebrowser/config/configtypes.py',
|
||||
'qutebrowser/config/configdata.py',
|
||||
'qutebrowser/config/configexc.py',
|
||||
'qutebrowser/config/textwrapper.py',
|
||||
|
||||
'qutebrowser/utils/qtutils.py',
|
||||
'qutebrowser/utils/standarddir.py',
|
||||
@ -56,6 +61,8 @@ PERFECT_FILES = [
|
||||
'qutebrowser/utils/usertypes.py',
|
||||
'qutebrowser/utils/utils.py',
|
||||
'qutebrowser/utils/version.py',
|
||||
'qutebrowser/utils/debug.py',
|
||||
'qutebrowser/utils/jinja.py',
|
||||
]
|
||||
|
||||
|
||||
@ -67,10 +74,23 @@ def main():
|
||||
"""
|
||||
utils.change_cwd()
|
||||
|
||||
if sys.platform != 'linux':
|
||||
print("Skipping coverage checks on non-Linux system.")
|
||||
sys.exit()
|
||||
elif '-k' in sys.argv[1:]:
|
||||
print("Skipping coverage checks because -k is given.")
|
||||
sys.exit()
|
||||
elif '-m' in sys.argv[1:]:
|
||||
print("Skipping coverage checks because -m is given.")
|
||||
sys.exit()
|
||||
elif any(arg.startswith('tests' + os.sep) for arg in sys.argv[1:]):
|
||||
print("Skipping coverage checks because a filename is given.")
|
||||
sys.exit()
|
||||
|
||||
for path in PERFECT_FILES:
|
||||
assert os.path.exists(os.path.join(*path.split('/'))), path
|
||||
|
||||
with open('coverage.xml', encoding='utf-8') as f:
|
||||
with open('.coverage.xml', encoding='utf-8') as f:
|
||||
tree = ElementTree.parse(f)
|
||||
classes = tree.getroot().findall('./packages/package/classes/class')
|
||||
|
||||
@ -101,6 +121,8 @@ def main():
|
||||
print("{} has 100% coverage but is not in PERFECT_FILES!".format(
|
||||
filename))
|
||||
|
||||
os.remove('.coverage.xml')
|
||||
|
||||
return status
|
||||
|
||||
|
||||
|
@ -52,6 +52,7 @@ def main():
|
||||
'redefined-outer-name',
|
||||
'unused-argument',
|
||||
'missing-docstring',
|
||||
'protected-access',
|
||||
# https://bitbucket.org/logilab/pylint/issue/511/
|
||||
'undefined-variable',
|
||||
]
|
||||
|
@ -24,7 +24,6 @@ import logging
|
||||
import pytest
|
||||
|
||||
from qutebrowser.browser import http
|
||||
from qutebrowser.utils import log
|
||||
|
||||
|
||||
DEFAULT_NAME = 'qutebrowser-download'
|
||||
@ -56,7 +55,7 @@ class HeaderChecker:
|
||||
"""Check if the passed header is ignored."""
|
||||
reply = self.stubs.FakeNetworkReply(
|
||||
headers={'Content-Disposition': header})
|
||||
with self.caplog.atLevel(logging.ERROR, logger=log.rfc6266.name):
|
||||
with self.caplog.atLevel(logging.ERROR, 'rfc6266'):
|
||||
# with self.assertLogs(log.rfc6266, logging.ERROR):
|
||||
cd_inline, cd_filename = http.parse_content_disposition(reply)
|
||||
assert cd_filename == DEFAULT_NAME
|
||||
|
@ -19,6 +19,8 @@
|
||||
|
||||
"""Hypothesis tests for qutebrowser.browser.http."""
|
||||
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
import hypothesis
|
||||
from hypothesis import strategies
|
||||
@ -35,11 +37,12 @@ from qutebrowser.browser import http, rfc6266
|
||||
'attachment; filename*={}',
|
||||
])
|
||||
@hypothesis.given(strategies.text(alphabet=[chr(x) for x in range(255)]))
|
||||
def test_parse_content_disposition(template, stubs, s):
|
||||
def test_parse_content_disposition(caplog, template, stubs, s):
|
||||
"""Test parsing headers based on templates which hypothesis completes."""
|
||||
header = template.format(s)
|
||||
reply = stubs.FakeNetworkReply(headers={'Content-Disposition': header})
|
||||
http.parse_content_disposition(reply)
|
||||
with caplog.atLevel(logging.ERROR, 'rfc6266'):
|
||||
http.parse_content_disposition(reply)
|
||||
|
||||
|
||||
@hypothesis.given(strategies.binary())
|
||||
|
35
tests/browser/network/test_schemehandler.py
Normal file
35
tests/browser/network/test_schemehandler.py
Normal file
@ -0,0 +1,35 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Tests for browser.network.schemehandler."""
|
||||
|
||||
import pytest
|
||||
|
||||
from qutebrowser.browser.network import schemehandler
|
||||
|
||||
|
||||
def test_init():
|
||||
handler = schemehandler.SchemeHandler(0)
|
||||
assert handler._win_id == 0
|
||||
|
||||
|
||||
def test_create_request():
|
||||
handler = schemehandler.SchemeHandler(0)
|
||||
with pytest.raises(NotImplementedError):
|
||||
handler.createRequest(None, None, None)
|
@ -17,8 +17,6 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
"""Tests for the webelement utils."""
|
||||
|
||||
from unittest import mock
|
||||
@ -334,6 +332,14 @@ class TestIsVisible:
|
||||
def frame(self, stubs):
|
||||
return stubs.FakeWebFrame(QRect(0, 0, 100, 100))
|
||||
|
||||
def test_invalid_frame_geometry(self, stubs):
|
||||
"""Test with an invalid frame geometry."""
|
||||
rect = QRect(0, 0, 0, 0)
|
||||
assert not rect.isValid()
|
||||
frame = stubs.FakeWebFrame(rect)
|
||||
elem = get_webelem(QRect(0, 0, 10, 10), frame)
|
||||
assert not elem.is_visible(frame)
|
||||
|
||||
def test_invalid_invisible(self, frame):
|
||||
"""Test elements with an invalid geometry which are invisible."""
|
||||
elem = get_webelem(QRect(0, 0, 0, 0), frame)
|
||||
@ -460,6 +466,67 @@ class TestIsVisibleIframe:
|
||||
assert not objects.elems[2].is_visible(objects.frame)
|
||||
assert objects.elems[3].is_visible(objects.frame)
|
||||
|
||||
@pytest.fixture
|
||||
def invalid_objects(self, stubs):
|
||||
"""Set up the following base situation:
|
||||
|
||||
0, 0 300, 0
|
||||
##############################
|
||||
# #
|
||||
0,10 # iframe 100,10 #
|
||||
#********** #
|
||||
#* e * elems[0]: 10, 10 in iframe (visible)
|
||||
#* * #
|
||||
#* * #
|
||||
#********** #
|
||||
0,110 #. .100,110 #
|
||||
#. . #
|
||||
#. e . elems[2]: 20,150 in iframe (not visible)
|
||||
#.......... #
|
||||
##############################
|
||||
300, 0 300, 300
|
||||
|
||||
Returns an Objects namedtuple with frame/iframe/elems attributes.
|
||||
"""
|
||||
frame = stubs.FakeWebFrame(QRect(0, 0, 300, 300))
|
||||
iframe = stubs.FakeWebFrame(QRect(0, 10, 100, 100), parent=frame)
|
||||
assert frame.geometry().contains(iframe.geometry())
|
||||
|
||||
elems = [
|
||||
get_webelem(QRect(10, 10, 0, 0), iframe),
|
||||
get_webelem(QRect(20, 150, 0, 0), iframe),
|
||||
]
|
||||
for e in elems:
|
||||
assert not e.geometry().isValid()
|
||||
|
||||
return self.Objects(frame=frame, iframe=iframe, elems=elems)
|
||||
|
||||
def test_invalid_visible(self, invalid_objects):
|
||||
"""Test elements with an invalid geometry which are visible.
|
||||
|
||||
This seems to happen sometimes in the real world, with real elements
|
||||
which *are* visible, but don't have a valid geometry.
|
||||
"""
|
||||
elem = invalid_objects.elems[0]
|
||||
assert elem.is_visible(invalid_objects.frame)
|
||||
|
||||
def test_invalid_invisible(self, invalid_objects):
|
||||
"""Test elements with an invalid geometry which are invisible."""
|
||||
assert not invalid_objects.elems[1].is_visible(invalid_objects.frame)
|
||||
|
||||
|
||||
def test_focus_element(stubs):
|
||||
"""Test getting focus element with a fake frame/element.
|
||||
|
||||
Testing this with a real webpage is almost impossible because the window
|
||||
and the element would have focus, which is hard to achieve consistently in
|
||||
a test.
|
||||
"""
|
||||
frame = stubs.FakeWebFrame(QRect(0, 0, 100, 100))
|
||||
elem = get_webelem()
|
||||
frame.focus_elem = elem._elem
|
||||
assert webelem.focus_elem(frame)._elem is elem._elem
|
||||
|
||||
|
||||
class TestRectOnView:
|
||||
|
||||
|
53
tests/completion/test_column_widths.py
Normal file
53
tests/completion/test_column_widths.py
Normal file
@ -0,0 +1,53 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2015 Alexander Cogneau <alexander.cogneau@gmail.com>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Tests for qutebrowser.completion.models column widths"""
|
||||
|
||||
import pytest
|
||||
|
||||
from qutebrowser.completion.models.base import BaseCompletionModel
|
||||
from qutebrowser.completion.models.configmodel import (
|
||||
SettingOptionCompletionModel, SettingSectionCompletionModel,
|
||||
SettingValueCompletionModel)
|
||||
from qutebrowser.completion.models.miscmodels import (
|
||||
CommandCompletionModel, HelpCompletionModel, QuickmarkCompletionModel,
|
||||
BookmarkCompletionModel, SessionCompletionModel)
|
||||
from qutebrowser.completion.models.urlmodel import UrlCompletionModel
|
||||
|
||||
|
||||
class TestColumnWidths:
|
||||
|
||||
"""Tests for the column widths of the completion models"""
|
||||
|
||||
CLASSES = [BaseCompletionModel, SettingOptionCompletionModel,
|
||||
SettingOptionCompletionModel, SettingSectionCompletionModel,
|
||||
SettingValueCompletionModel, CommandCompletionModel,
|
||||
HelpCompletionModel, QuickmarkCompletionModel,
|
||||
BookmarkCompletionModel, SessionCompletionModel,
|
||||
UrlCompletionModel]
|
||||
|
||||
@pytest.mark.parametrize("model", CLASSES)
|
||||
def test_list_size(self, model):
|
||||
"""Test if there are 3 items in the COLUMN_WIDTHS property"""
|
||||
assert len(model.COLUMN_WIDTHS) == 3
|
||||
|
||||
@pytest.mark.parametrize("model", CLASSES)
|
||||
def test_column_width_sum(self, model):
|
||||
"""Test if the sum of the widths asserts to 100"""
|
||||
assert sum(model.COLUMN_WIDTHS) == 100
|
@ -16,8 +16,6 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
"""Tests for qutebrowser.config.config."""
|
||||
|
||||
import os
|
||||
@ -234,10 +232,12 @@ class TestKeyConfigParser:
|
||||
assert new == new_expected
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestDefaultConfig:
|
||||
|
||||
"""Test validating of the default config."""
|
||||
|
||||
@pytest.mark.usefixtures('qapp')
|
||||
def test_default_config(self):
|
||||
"""Test validating of the default config."""
|
||||
conf = config.ConfigManager(None, None)
|
||||
@ -254,6 +254,7 @@ class TestDefaultConfig:
|
||||
runner.parse(cmd, aliases=False)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestConfigInit:
|
||||
|
||||
"""Test initializing of the config."""
|
||||
@ -272,8 +273,10 @@ class TestConfigInit:
|
||||
objreg.register('save-manager', mock.MagicMock())
|
||||
args = argparse.Namespace(relaxed_config=False)
|
||||
objreg.register('args', args)
|
||||
old_standarddir_args = standarddir._args
|
||||
yield
|
||||
objreg.global_registry.clear()
|
||||
standarddir._args = old_standarddir_args
|
||||
|
||||
def test_config_none(self, monkeypatch):
|
||||
"""Test initializing with config path set to None."""
|
||||
|
@ -16,13 +16,12 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
"""Tests for qutebrowser.config.configtypes."""
|
||||
|
||||
import re
|
||||
import collections
|
||||
import itertools
|
||||
import os.path
|
||||
import base64
|
||||
|
||||
import pytest
|
||||
@ -1076,6 +1075,7 @@ class TestRegexList:
|
||||
assert klass().transform(val) == expected
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('qapp')
|
||||
class TestFileAndUserStyleSheet:
|
||||
|
||||
"""Test File/UserStyleSheet."""
|
||||
@ -1146,6 +1146,26 @@ class TestFileAndUserStyleSheet:
|
||||
with pytest.raises(configexc.ValidationError):
|
||||
configtypes.File().validate('foobar')
|
||||
|
||||
@pytest.mark.parametrize('configtype, value, raises', [
|
||||
(configtypes.File(), 'foobar', True),
|
||||
(configtypes.UserStyleSheet(), 'foobar', False),
|
||||
(configtypes.UserStyleSheet(), '\ud800', True),
|
||||
])
|
||||
def test_validate_rel_inexistent(self, os_mock, monkeypatch, configtype,
|
||||
value, raises):
|
||||
"""Test with a relative path and standarddir.config returning None."""
|
||||
monkeypatch.setattr(
|
||||
'qutebrowser.config.configtypes.standarddir.config',
|
||||
lambda: 'this/does/not/exist')
|
||||
os_mock.path.isabs.return_value = False
|
||||
os_mock.path.isfile.side_effect = os.path.isfile
|
||||
|
||||
if raises:
|
||||
with pytest.raises(configexc.ValidationError):
|
||||
configtype.validate(value)
|
||||
else:
|
||||
configtype.validate(value)
|
||||
|
||||
def test_validate_expanduser(self, klass, os_mock):
|
||||
"""Test if validate expands the user correctly."""
|
||||
os_mock.path.isfile.side_effect = (lambda path:
|
||||
|
38
tests/config/test_textwrapper.py
Normal file
38
tests/config/test_textwrapper.py
Normal file
@ -0,0 +1,38 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Tests for config.textwrapper."""
|
||||
|
||||
from qutebrowser.config import textwrapper
|
||||
|
||||
|
||||
def test_default_args():
|
||||
wrapper = textwrapper.TextWrapper()
|
||||
assert wrapper.width == 72
|
||||
assert not wrapper.replace_whitespace
|
||||
assert not wrapper.break_long_words
|
||||
assert not wrapper.break_on_hyphens
|
||||
assert wrapper.initial_indent == '# '
|
||||
assert wrapper.subsequent_indent == '# '
|
||||
|
||||
|
||||
def test_custom_args():
|
||||
wrapper = textwrapper.TextWrapper(drop_whitespace=False)
|
||||
assert wrapper.width == 72
|
||||
assert not wrapper.drop_whitespace
|
@ -23,22 +23,24 @@ import os
|
||||
import sys
|
||||
import collections
|
||||
import itertools
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
|
||||
import stubs as stubsmod
|
||||
import logfail
|
||||
from qutebrowser.config import configexc
|
||||
from qutebrowser.utils import objreg, usertypes
|
||||
|
||||
|
||||
@pytest.fixture(scope='session', autouse=True)
|
||||
def app_and_logging(qapp):
|
||||
"""Initialize a QApplication and logging.
|
||||
|
||||
This ensures that a QApplication is created and used by all tests.
|
||||
"""
|
||||
from log import init
|
||||
init()
|
||||
@pytest.yield_fixture(scope='session', autouse=True)
|
||||
def fail_on_logging():
|
||||
handler = logfail.LogFailHandler()
|
||||
logging.getLogger().addHandler(handler)
|
||||
yield
|
||||
logging.getLogger().removeHandler(handler)
|
||||
handler.close()
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
@ -58,7 +60,7 @@ def unicode_encode_err():
|
||||
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def qnam():
|
||||
def qnam(qapp):
|
||||
"""Session-wide QNetworkAccessManager."""
|
||||
from PyQt5.QtNetwork import QNetworkAccessManager
|
||||
nam = QNetworkAccessManager()
|
||||
@ -118,8 +120,14 @@ def pytest_collection_modifyitems(items):
|
||||
http://pytest.org/latest/plugins.html
|
||||
"""
|
||||
for item in items:
|
||||
if 'qtbot' in getattr(item, 'fixturenames', ()):
|
||||
if 'qapp' in getattr(item, 'fixturenames', ()):
|
||||
item.add_marker('gui')
|
||||
if sys.platform == 'linux' and not os.environ.get('DISPLAY', ''):
|
||||
if 'CI' in os.environ:
|
||||
raise Exception("No display available on CI!")
|
||||
skip_marker = pytest.mark.skipif(
|
||||
True, reason="No DISPLAY available")
|
||||
item.add_marker(skip_marker)
|
||||
|
||||
|
||||
def _generate_cmdline_tests():
|
||||
|
@ -67,12 +67,14 @@ def caret_tester(js_tester):
|
||||
return CaretTester(js_tester)
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_simple(caret_tester):
|
||||
"""Test with a simple (one-line) HTML text."""
|
||||
caret_tester.js.load('position_caret/simple.html')
|
||||
caret_tester.check()
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_scrolled_down(caret_tester):
|
||||
"""Test with multiple text blocks with the viewport scrolled down."""
|
||||
caret_tester.js.load('position_caret/scrolled_down.html')
|
||||
@ -81,6 +83,7 @@ def test_scrolled_down(caret_tester):
|
||||
caret_tester.check()
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.parametrize('style', ['visibility: hidden', 'display: none'])
|
||||
def test_invisible(caret_tester, style):
|
||||
"""Test with hidden text elements."""
|
||||
@ -88,6 +91,7 @@ def test_invisible(caret_tester, style):
|
||||
caret_tester.check()
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_scrolled_down_img(caret_tester):
|
||||
"""Test with an image at the top with the viewport scrolled down."""
|
||||
caret_tester.js.load('position_caret/scrolled_down_img.html')
|
||||
|
@ -17,8 +17,6 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
"""Tests for BaseKeyParser."""
|
||||
|
||||
import sys
|
||||
@ -29,7 +27,6 @@ from PyQt5.QtCore import Qt
|
||||
import pytest
|
||||
|
||||
from qutebrowser.keyinput import basekeyparser
|
||||
from qutebrowser.utils import log
|
||||
|
||||
|
||||
CONFIG = {'input': {'timeout': 100}}
|
||||
@ -107,7 +104,7 @@ class TestSpecialKeys:
|
||||
def setup(self, caplog, fake_keyconfig):
|
||||
self.kp = basekeyparser.BaseKeyParser(0)
|
||||
self.kp.execute = mock.Mock()
|
||||
with caplog.atLevel(logging.WARNING, log.keyboard.name):
|
||||
with caplog.atLevel(logging.WARNING, 'keyboard'):
|
||||
# Ignoring keychain 'ccc' in mode 'test' because keychains are not
|
||||
# supported there.
|
||||
self.kp.read_config('test')
|
||||
@ -186,7 +183,7 @@ class TestKeyChain:
|
||||
self.kp.execute.assert_called_once_with('0', self.kp.Type.chain, None)
|
||||
assert self.kp._keystring == ''
|
||||
|
||||
def test_ambiguous_keychain(self, fake_keyevent_factory, config_stub,
|
||||
def test_ambiguous_keychain(self, qapp, fake_keyevent_factory, config_stub,
|
||||
monkeypatch):
|
||||
"""Test ambiguous keychain."""
|
||||
config_stub.data = CONFIG
|
||||
|
@ -39,8 +39,6 @@ class TestsNormalKeyParser:
|
||||
kp: The NormalKeyParser to be tested.
|
||||
"""
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
@pytest.yield_fixture(autouse=True)
|
||||
def setup(self, monkeypatch, stubs, config_stub, fake_keyconfig):
|
||||
"""Set up mocks and read the test config."""
|
||||
|
68
tests/log.py
68
tests/log.py
@ -1,68 +0,0 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Logging setup for the tests."""
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt5.QtCore import (QtDebugMsg, QtWarningMsg, QtCriticalMsg, QtFatalMsg,
|
||||
qInstallMessageHandler)
|
||||
|
||||
|
||||
def init():
|
||||
"""Initialize logging for the tests."""
|
||||
logging.basicConfig(format='\nLOG %(levelname)s %(name)s '
|
||||
'%(module)s:%(funcName)s:%(lineno)d %(message)s',
|
||||
level=logging.WARNING)
|
||||
logging.captureWarnings(True)
|
||||
qInstallMessageHandler(qt_message_handler)
|
||||
|
||||
|
||||
def qt_message_handler(msg_type, context, msg):
|
||||
"""Qt message handler to redirect qWarning etc. to the logging system.
|
||||
|
||||
Args:
|
||||
QtMsgType msg_type: The level of the message.
|
||||
QMessageLogContext context: The source code location of the message.
|
||||
msg: The message text.
|
||||
"""
|
||||
# Mapping from Qt logging levels to the matching logging module levels.
|
||||
# Note we map critical to ERROR as it's actually "just" an error, and fatal
|
||||
# to critical.
|
||||
qt_to_logging = {
|
||||
QtDebugMsg: logging.DEBUG,
|
||||
QtWarningMsg: logging.WARNING,
|
||||
QtCriticalMsg: logging.ERROR,
|
||||
QtFatalMsg: logging.CRITICAL,
|
||||
}
|
||||
level = qt_to_logging[msg_type]
|
||||
# There's very similar code in utils.log, but we want it duplicated here
|
||||
# for the tests.
|
||||
if context.function is None:
|
||||
func = 'none'
|
||||
else:
|
||||
func = context.function
|
||||
if context.category is None or context.category == 'default':
|
||||
name = 'qt'
|
||||
else:
|
||||
name = 'qt-' + context.category
|
||||
logger = logging.getLogger('qt-tests')
|
||||
record = logger.makeRecord(name, level, context.file, context.line, msg,
|
||||
None, None, func)
|
||||
logger.handle(record)
|
67
tests/logfail.py
Normal file
67
tests/logfail.py
Normal file
@ -0,0 +1,67 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Logging handling for the tests."""
|
||||
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
|
||||
try:
|
||||
import pytest_capturelog
|
||||
except ImportError:
|
||||
# When using pytest for pyflakes/pep8/..., the plugin won't be available
|
||||
# but conftest.py will still be loaded.
|
||||
#
|
||||
# However, LogFailHandler.emit will never be used in that case, so we just
|
||||
# ignore the ImportError.
|
||||
pass
|
||||
|
||||
|
||||
class LogFailHandler(logging.Handler):
|
||||
|
||||
"""A logging handler which makes tests fail on unexpected messages."""
|
||||
|
||||
def __init__(self, level=logging.NOTSET, min_level=logging.WARNING):
|
||||
self._min_level = min_level
|
||||
super().__init__(level)
|
||||
|
||||
def emit(self, record):
|
||||
logger = logging.getLogger(record.name)
|
||||
root_logger = logging.getLogger()
|
||||
|
||||
for h in root_logger.handlers:
|
||||
if isinstance(h, pytest_capturelog.CaptureLogHandler):
|
||||
caplog_handler = h
|
||||
break
|
||||
else:
|
||||
# The CaptureLogHandler is not available anymore during fixture
|
||||
# teardown, so we ignore logging messages emitted there..
|
||||
return
|
||||
|
||||
if (logger.level == record.levelno or
|
||||
caplog_handler.level == record.levelno):
|
||||
# caplog.atLevel(...) was used with the level of this message, i.e.
|
||||
# it was expected.
|
||||
return
|
||||
if record.levelno < self._min_level:
|
||||
return
|
||||
pytest.fail("Got logging message on logger {} with level {}: "
|
||||
"{}!".format(record.name, record.levelname,
|
||||
record.getMessage()))
|
@ -50,4 +50,54 @@ def test_elided_text(qtbot, elidemode, check):
|
||||
label.setText(long_string)
|
||||
label.resize(100, 50)
|
||||
label.show()
|
||||
assert check(label._elided_text) # pylint: disable=protected-access
|
||||
qtbot.waitForWindowShown(label)
|
||||
assert check(label._elided_text)
|
||||
|
||||
|
||||
def test_settext_empty(mocker, qtbot):
|
||||
"""Make sure using setText('') works and runs repaint."""
|
||||
label = TextBase()
|
||||
qtbot.add_widget(label)
|
||||
mocker.patch('qutebrowser.mainwindow.statusbar.textbase.TextBase.repaint',
|
||||
autospec=True)
|
||||
|
||||
label.setText('')
|
||||
label.repaint.assert_called_with()
|
||||
|
||||
|
||||
def test_resize(qtbot):
|
||||
"""Make sure the elided text is updated when resizing."""
|
||||
label = TextBase()
|
||||
qtbot.add_widget(label)
|
||||
long_string = 'Hello world! ' * 20
|
||||
label.setText(long_string)
|
||||
|
||||
label.show()
|
||||
qtbot.waitForWindowShown(label)
|
||||
|
||||
text_1 = label._elided_text
|
||||
label.resize(20, 50)
|
||||
text_2 = label._elided_text
|
||||
|
||||
assert text_1 != text_2
|
||||
|
||||
|
||||
def test_text_elide_none(mocker, qtbot):
|
||||
"""Make sure the text doesn't get elided if it's empty."""
|
||||
label = TextBase()
|
||||
qtbot.add_widget(label)
|
||||
label.setText('')
|
||||
mocker.patch('qutebrowser.mainwindow.statusbar.textbase.TextBase.'
|
||||
'fontMetrics')
|
||||
label._update_elided_text(20)
|
||||
|
||||
assert not label.fontMetrics.called
|
||||
|
||||
|
||||
def test_unset_text(qtbot):
|
||||
"""Make sure the text is cleared properly."""
|
||||
label = TextBase()
|
||||
qtbot.add_widget(label)
|
||||
label.setText('foo')
|
||||
label.setText('')
|
||||
assert not label._elided_text
|
||||
|
@ -19,8 +19,6 @@
|
||||
|
||||
"""Tests for qutebrowser.misc.editor."""
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import logging
|
||||
@ -46,7 +44,7 @@ class TestArg:
|
||||
stubs.fake_qprocess())
|
||||
self.editor = editor.ExternalEditor(0)
|
||||
yield
|
||||
self.editor._cleanup() # pylint: disable=protected-access
|
||||
self.editor._cleanup()
|
||||
|
||||
@pytest.fixture
|
||||
def stubbed_config(self, config_stub, monkeypatch):
|
||||
@ -229,18 +227,18 @@ class TestErrorMessage:
|
||||
monkeypatch.setattr('qutebrowser.misc.editor.config', config_stub)
|
||||
self.editor = editor.ExternalEditor(0)
|
||||
yield
|
||||
self.editor._cleanup() # pylint: disable=protected-access
|
||||
self.editor._cleanup()
|
||||
|
||||
def test_proc_error(self, caplog):
|
||||
"""Test on_proc_error."""
|
||||
self.editor.edit("")
|
||||
with caplog.atLevel(logging.ERROR, 'message'):
|
||||
with caplog.atLevel(logging.ERROR):
|
||||
self.editor.on_proc_error(QProcess.Crashed)
|
||||
assert len(caplog.records()) == 2
|
||||
|
||||
def test_proc_return(self, caplog):
|
||||
"""Test on_proc_finished with a bad exit status."""
|
||||
self.editor.edit("")
|
||||
with caplog.atLevel(logging.ERROR, 'message'):
|
||||
with caplog.atLevel(logging.ERROR):
|
||||
self.editor.on_proc_closed(1, QProcess.NormalExit)
|
||||
assert len(caplog.records()) == 3
|
||||
assert len(caplog.records()) == 2
|
||||
|
@ -17,12 +17,12 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
"""Tests for qutebrowser.misc.guiprocess."""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
from PyQt5.QtCore import QProcess
|
||||
@ -97,6 +97,7 @@ def test_double_start(qtbot, proc):
|
||||
|
||||
|
||||
@pytest.mark.not_frozen
|
||||
@pytest.mark.skipif(os.name == 'nt', reason="Test is flaky on Windows...")
|
||||
def test_double_start_finished(qtbot, proc):
|
||||
"""Test starting a GUIProcess twice (with the first call finished)."""
|
||||
with qtbot.waitSignals([proc.started, proc.finished], raising=True,
|
||||
@ -117,10 +118,13 @@ def test_cmd_args(fake_proc):
|
||||
assert (fake_proc.cmd, fake_proc.args) == (cmd, args)
|
||||
|
||||
|
||||
def test_error(qtbot, proc):
|
||||
# WORKAROUND for https://github.com/pytest-dev/pytest-qt/issues/67
|
||||
@pytest.mark.skipif(os.name == 'nt', reason="Test is flaky on Windows...")
|
||||
def test_error(qtbot, proc, caplog):
|
||||
"""Test the process emitting an error."""
|
||||
with qtbot.waitSignal(proc.error, raising=True):
|
||||
proc.start('this_does_not_exist_either', [])
|
||||
with caplog.atLevel(logging.ERROR, 'message'):
|
||||
with qtbot.waitSignal(proc.error, raising=True):
|
||||
proc.start('this_does_not_exist_either', [])
|
||||
|
||||
|
||||
@pytest.mark.not_frozen
|
||||
|
@ -17,8 +17,6 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
"""Tests for qutebrowser.misc.lineparser."""
|
||||
|
||||
import io
|
||||
|
@ -19,8 +19,6 @@
|
||||
|
||||
"""Tests for qutebrowser.misc.readline."""
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
import re
|
||||
import inspect
|
||||
|
||||
|
@ -42,7 +42,11 @@ class FakeKeyEvent:
|
||||
|
||||
class FakeWebFrame:
|
||||
|
||||
"""A stub for QWebFrame."""
|
||||
"""A stub for QWebFrame.
|
||||
|
||||
Attributes:
|
||||
focus_elem: The 'focused' element.
|
||||
"""
|
||||
|
||||
def __init__(self, geometry, scroll=None, parent=None):
|
||||
"""Constructor.
|
||||
@ -57,6 +61,16 @@ class FakeWebFrame:
|
||||
self.geometry = mock.Mock(return_value=geometry)
|
||||
self.scrollPosition = mock.Mock(return_value=scroll)
|
||||
self.parentFrame = mock.Mock(return_value=parent)
|
||||
self.focus_elem = None
|
||||
|
||||
def findFirstElement(self, selector):
|
||||
if selector == '*:focus':
|
||||
if self.focus_elem is not None:
|
||||
return self.focus_elem
|
||||
else:
|
||||
raise Exception("Trying to get focus element but it's unset!")
|
||||
else:
|
||||
raise Exception("Unknown selector {!r}!".format(selector))
|
||||
|
||||
|
||||
class FakeChildrenFrame:
|
||||
@ -73,11 +87,14 @@ class FakeQApplication:
|
||||
|
||||
"""Stub to insert as QApplication module."""
|
||||
|
||||
def __init__(self, style=None):
|
||||
def __init__(self, style=None, all_widgets=None):
|
||||
self.instance = mock.Mock(return_value=self)
|
||||
|
||||
self.style = mock.Mock(spec=QCommonStyle)
|
||||
self.style().metaObject().className.return_value = style
|
||||
|
||||
self.allWidgets = lambda: all_widgets
|
||||
|
||||
|
||||
class FakeUrl:
|
||||
|
||||
|
65
tests/test_logfail.py
Normal file
65
tests/test_logfail.py
Normal file
@ -0,0 +1,65 @@
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
# Copyright 2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Tests for the LogFailHandler test helper."""
|
||||
|
||||
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def test_log_debug():
|
||||
logging.debug('foo')
|
||||
|
||||
|
||||
def test_log_warning():
|
||||
with pytest.raises(pytest.fail.Exception):
|
||||
logging.warning('foo')
|
||||
|
||||
|
||||
def test_log_expected(caplog):
|
||||
with caplog.atLevel(logging.ERROR):
|
||||
logging.error('foo')
|
||||
|
||||
|
||||
def test_log_expected_logger(caplog):
|
||||
logger = 'logfail_test_logger'
|
||||
with caplog.atLevel(logging.ERROR, logger):
|
||||
logging.getLogger(logger).error('foo')
|
||||
|
||||
|
||||
def test_log_expected_wrong_level(caplog):
|
||||
with pytest.raises(pytest.fail.Exception):
|
||||
with caplog.atLevel(logging.ERROR):
|
||||
logging.critical('foo')
|
||||
|
||||
|
||||
def test_log_expected_logger_wrong_level(caplog):
|
||||
logger = 'logfail_test_logger'
|
||||
with pytest.raises(pytest.fail.Exception):
|
||||
with caplog.atLevel(logging.ERROR, logger):
|
||||
logging.getLogger(logger).critical('foo')
|
||||
|
||||
|
||||
def test_log_expected_wrong_logger(caplog):
|
||||
logger = 'logfail_test_logger'
|
||||
with pytest.raises(pytest.fail.Exception):
|
||||
with caplog.atLevel(logging.ERROR, logger):
|
||||
logging.error('foo')
|
@ -1,45 +0,0 @@
|
||||
# Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Tests for qutebrowser.utils.debug.log_time."""
|
||||
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
|
||||
from qutebrowser.utils import debug
|
||||
|
||||
|
||||
def test_log_time(caplog):
|
||||
"""Test if log_time logs properly."""
|
||||
logger_name = 'qt-tests'
|
||||
|
||||
with caplog.atLevel(logging.DEBUG, logger=logger_name):
|
||||
with debug.log_time(logging.getLogger(logger_name), action='foobar'):
|
||||
time.sleep(0.1)
|
||||
|
||||
records = caplog.records()
|
||||
assert len(records) == 1
|
||||
|
||||
pattern = re.compile(r'^Foobar took ([\d.]*) seconds\.$')
|
||||
match = pattern.match(records[0].msg)
|
||||
assert match
|
||||
|
||||
duration = float(match.group(1))
|
||||
assert 0 < duration < 1
|
@ -1,64 +0,0 @@
|
||||
# Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Tests for qutebrowser.utils.debug.qenum_key."""
|
||||
|
||||
import pytest
|
||||
|
||||
from PyQt5.QtWidgets import QStyle, QFrame
|
||||
|
||||
from qutebrowser.utils import debug
|
||||
|
||||
|
||||
def test_no_metaobj():
|
||||
"""Test with an enum with no meta-object."""
|
||||
assert not hasattr(QStyle.PrimitiveElement, 'staticMetaObject')
|
||||
key = debug.qenum_key(QStyle, QStyle.PE_PanelButtonCommand)
|
||||
assert key == 'PE_PanelButtonCommand'
|
||||
|
||||
|
||||
def test_metaobj():
|
||||
"""Test with an enum with meta-object."""
|
||||
assert hasattr(QFrame, 'staticMetaObject')
|
||||
key = debug.qenum_key(QFrame, QFrame.Sunken)
|
||||
assert key == 'Sunken'
|
||||
|
||||
|
||||
def test_add_base():
|
||||
"""Test with add_base=True."""
|
||||
key = debug.qenum_key(QFrame, QFrame.Sunken, add_base=True)
|
||||
assert key == 'QFrame.Sunken'
|
||||
|
||||
|
||||
def test_int_noklass():
|
||||
"""Test passing an int without explicit klass given."""
|
||||
with pytest.raises(TypeError):
|
||||
debug.qenum_key(QFrame, 42)
|
||||
|
||||
|
||||
def test_int():
|
||||
"""Test passing an int with explicit klass given."""
|
||||
key = debug.qenum_key(QFrame, 0x0030, klass=QFrame.Shadow)
|
||||
assert key == 'Sunken'
|
||||
|
||||
|
||||
def test_unknown():
|
||||
"""Test passing an unknown value."""
|
||||
key = debug.qenum_key(QFrame, 0x1337, klass=QFrame.Shadow)
|
||||
assert key == '0x1337'
|
@ -1,77 +0,0 @@
|
||||
# Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Tests for qutebrowser.utils.debug.qflags_key.
|
||||
|
||||
https://github.com/The-Compiler/qutebrowser/issues/42
|
||||
"""
|
||||
|
||||
import pytest
|
||||
|
||||
from PyQt5.QtCore import Qt
|
||||
from qutebrowser.utils import debug
|
||||
|
||||
|
||||
fixme = pytest.mark.xfail(reason="See issue #42", raises=AssertionError)
|
||||
|
||||
|
||||
@fixme
|
||||
def test_single():
|
||||
"""Test with single value."""
|
||||
flags = debug.qflags_key(Qt, Qt.AlignTop)
|
||||
assert flags == 'AlignTop'
|
||||
|
||||
|
||||
@fixme
|
||||
def test_multiple():
|
||||
"""Test with multiple values."""
|
||||
flags = debug.qflags_key(Qt, Qt.AlignLeft | Qt.AlignTop)
|
||||
assert flags == 'AlignLeft|AlignTop'
|
||||
|
||||
|
||||
def test_combined():
|
||||
"""Test with a combined value."""
|
||||
flags = debug.qflags_key(Qt, Qt.AlignCenter)
|
||||
assert flags == 'AlignHCenter|AlignVCenter'
|
||||
|
||||
|
||||
@fixme
|
||||
def test_add_base():
|
||||
"""Test with add_base=True."""
|
||||
flags = debug.qflags_key(Qt, Qt.AlignTop, add_base=True)
|
||||
assert flags == 'Qt.AlignTop'
|
||||
|
||||
|
||||
def test_int_noklass():
|
||||
"""Test passing an int without explicit klass given."""
|
||||
with pytest.raises(TypeError):
|
||||
debug.qflags_key(Qt, 42)
|
||||
|
||||
|
||||
@fixme
|
||||
def test_int():
|
||||
"""Test passing an int with explicit klass given."""
|
||||
flags = debug.qflags_key(Qt, 0x0021, klass=Qt.Alignment)
|
||||
assert flags == 'AlignLeft|AlignTop'
|
||||
|
||||
|
||||
def test_unknown():
|
||||
"""Test passing an unknown value."""
|
||||
flags = debug.qflags_key(Qt, 0x1100, klass=Qt.Alignment)
|
||||
assert flags == '0x0100|0x1000'
|
@ -1,52 +0,0 @@
|
||||
# Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Test signal debug output functions."""
|
||||
|
||||
import pytest
|
||||
|
||||
from qutebrowser.utils import debug
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def signal(stubs):
|
||||
"""Fixture to provide a faked pyqtSignal."""
|
||||
return stubs.FakeSignal()
|
||||
|
||||
|
||||
def test_signal_name(signal):
|
||||
"""Test signal_name()."""
|
||||
assert debug.signal_name(signal) == 'fake'
|
||||
|
||||
|
||||
def test_dbg_signal(signal):
|
||||
"""Test dbg_signal()."""
|
||||
assert debug.dbg_signal(signal, [23, 42]) == 'fake(23, 42)'
|
||||
|
||||
|
||||
def test_dbg_signal_eliding(signal):
|
||||
"""Test eliding in dbg_signal()."""
|
||||
dbg_signal = debug.dbg_signal(signal, ['x' * 201])
|
||||
assert dbg_signal == "fake('{}\u2026)".format('x' * 198)
|
||||
|
||||
|
||||
def test_dbg_signal_newline(signal):
|
||||
"""Test dbg_signal() with a newline."""
|
||||
dbg_signal = debug.dbg_signal(signal, ['foo\nbar'])
|
||||
assert dbg_signal == r"fake('foo\nbar')"
|
252
tests/utils/test_debug.py
Normal file
252
tests/utils/test_debug.py
Normal file
@ -0,0 +1,252 @@
|
||||
# Copyright 2014-2015 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
|
||||
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||
|
||||
#
|
||||
# This file is part of qutebrowser.
|
||||
#
|
||||
# qutebrowser is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# qutebrowser is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""Tests for qutebrowser.utils.debug."""
|
||||
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
import textwrap
|
||||
|
||||
import pytest
|
||||
from PyQt5.QtCore import pyqtSignal, Qt, QEvent, QObject
|
||||
from PyQt5.QtWidgets import QStyle, QFrame
|
||||
|
||||
from qutebrowser.utils import debug
|
||||
|
||||
|
||||
@debug.log_events
|
||||
class EventObject(QObject):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def test_log_events(qapp, caplog):
|
||||
obj = EventObject()
|
||||
qapp.postEvent(obj, QEvent(QEvent.User))
|
||||
qapp.processEvents()
|
||||
records = caplog.records()
|
||||
assert len(records) == 1
|
||||
assert records[0].msg == 'Event in test_debug.EventObject: User'
|
||||
|
||||
|
||||
class SignalObject(QObject):
|
||||
|
||||
signal1 = pyqtSignal()
|
||||
signal2 = pyqtSignal(str, str)
|
||||
|
||||
def __repr__(self):
|
||||
"""This is not a nice thing to do, but it makes our tests easier."""
|
||||
return '<repr>'
|
||||
|
||||
|
||||
@debug.log_signals
|
||||
class DecoratedSignalObject(SignalObject):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
@pytest.fixture(params=[(SignalObject, True), (DecoratedSignalObject, False)])
|
||||
def signal_obj(request):
|
||||
klass, wrap = request.param
|
||||
obj = klass()
|
||||
if wrap:
|
||||
debug.log_signals(obj)
|
||||
return obj
|
||||
|
||||
|
||||
def test_log_signals(caplog, signal_obj):
|
||||
signal_obj.signal1.emit()
|
||||
signal_obj.signal2.emit('foo', 'bar')
|
||||
|
||||
records = caplog.records()
|
||||
assert len(records) == 2
|
||||
assert records[0].msg == 'Signal in <repr>: signal1()'
|
||||
assert records[1].msg == "Signal in <repr>: signal2('foo', 'bar')"
|
||||
|
||||
|
||||
def test_log_time(caplog):
|
||||
logger_name = 'qt-tests'
|
||||
|
||||
with caplog.atLevel(logging.DEBUG, logger_name):
|
||||
with debug.log_time(logging.getLogger(logger_name), action='foobar'):
|
||||
time.sleep(0.1)
|
||||
|
||||
records = caplog.records()
|
||||
assert len(records) == 1
|
||||
|
||||
pattern = re.compile(r'^Foobar took ([\d.]*) seconds\.$')
|
||||
match = pattern.match(records[0].msg)
|
||||
assert match
|
||||
|
||||
duration = float(match.group(1))
|
||||
assert 0 < duration < 1
|
||||
|
||||
|
||||
class TestQEnumKey:
|
||||
|
||||
def test_metaobj(self):
|
||||
"""Make sure the classes we use in the tests have a metaobj or not.
|
||||
|
||||
If Qt/PyQt even changes and our tests wouldn't test the full
|
||||
functionality of qenum_key because of that, this test will tell us.
|
||||
"""
|
||||
assert not hasattr(QStyle.PrimitiveElement, 'staticMetaObject')
|
||||
assert hasattr(QFrame, 'staticMetaObject')
|
||||
|
||||
@pytest.mark.parametrize('base, value, klass, expected', [
|
||||
(QStyle, QStyle.PE_PanelButtonCommand, None, 'PE_PanelButtonCommand'),
|
||||
(QFrame, QFrame.Sunken, None, 'Sunken'),
|
||||
(QFrame, 0x0030, QFrame.Shadow, 'Sunken'),
|
||||
(QFrame, 0x1337, QFrame.Shadow, '0x1337'),
|
||||
])
|
||||
def test_qenum_key(self, base, value, klass, expected):
|
||||
key = debug.qenum_key(base, value, klass=klass)
|
||||
assert key == expected
|
||||
|
||||
def test_add_base(self):
|
||||
key = debug.qenum_key(QFrame, QFrame.Sunken, add_base=True)
|
||||
assert key == 'QFrame.Sunken'
|
||||
|
||||
def test_int_noklass(self):
|
||||
"""Test passing an int without explicit klass given."""
|
||||
with pytest.raises(TypeError):
|
||||
debug.qenum_key(QFrame, 42)
|
||||
|
||||
|
||||
class TestQFlagsKey:
|
||||
|
||||
"""Tests for qutebrowser.utils.debug.qflags_key.
|
||||
|
||||
https://github.com/The-Compiler/qutebrowser/issues/42
|
||||
"""
|
||||
|
||||
fixme = pytest.mark.xfail(reason="See issue #42", raises=AssertionError)
|
||||
|
||||
@pytest.mark.parametrize('base, value, klass, expected', [
|
||||
fixme((Qt, Qt.AlignTop, None, 'AlignTop')),
|
||||
fixme((Qt, Qt.AlignLeft | Qt.AlignTop, None, 'AlignLeft|AlignTop')),
|
||||
(Qt, Qt.AlignCenter, None, 'AlignHCenter|AlignVCenter'),
|
||||
fixme((Qt, 0x0021, Qt.Alignment, 'AlignLeft|AlignTop')),
|
||||
(Qt, 0x1100, Qt.Alignment, '0x0100|0x1000'),
|
||||
])
|
||||
def test_qflags_key(self, base, value, klass, expected):
|
||||
flags = debug.qflags_key(base, value, klass=klass)
|
||||
assert flags == expected
|
||||
|
||||
@fixme
|
||||
def test_add_base(self):
|
||||
"""Test with add_base=True."""
|
||||
flags = debug.qflags_key(Qt, Qt.AlignTop, add_base=True)
|
||||
assert flags == 'Qt.AlignTop'
|
||||
|
||||
def test_int_noklass(self):
|
||||
"""Test passing an int without explicit klass given."""
|
||||
with pytest.raises(TypeError):
|
||||
debug.qflags_key(Qt, 42)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('signal, expected', [
|
||||
(SignalObject().signal1, 'signal1'),
|
||||
(SignalObject().signal2, 'signal2'),
|
||||
])
|
||||
def test_signal_name(signal, expected):
|
||||
assert debug.signal_name(signal) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize('args, kwargs, expected', [
|
||||
([], {}, ''),
|
||||
(None, None, ''),
|
||||
(['foo'], None, "'foo'"),
|
||||
(['foo', 'bar'], None, "'foo', 'bar'"),
|
||||
(None, {'foo': 'bar'}, "foo='bar'"),
|
||||
(['foo', 'bar'], {'baz': 'fish'}, "'foo', 'bar', baz='fish'"),
|
||||
(['x' * 300], None, "'{}".format('x' * 198 + '…')),
|
||||
])
|
||||
def test_format_args(args, kwargs, expected):
|
||||
assert debug.format_args(args, kwargs) == expected
|
||||
|
||||
|
||||
def func():
|
||||
pass
|
||||
|
||||
|
||||
@pytest.mark.parametrize('func, args, kwargs, full, expected', [
|
||||
(func, None, None, False, 'func()'),
|
||||
(func, [1, 2], None, False, 'func(1, 2)'),
|
||||
(func, [1, 2], None, True, 'test_debug.func(1, 2)'),
|
||||
(func, [1, 2], {'foo': 3}, False, 'func(1, 2, foo=3)'),
|
||||
])
|
||||
def test_format_call(func, args, kwargs, full, expected):
|
||||
assert debug.format_call(func, args, kwargs, full) == expected
|
||||
|
||||
|
||||
@pytest.mark.parametrize('args, expected', [
|
||||
([23, 42], 'fake(23, 42)'),
|
||||
(['x' * 201], "fake('{}\u2026)".format('x' * 198)),
|
||||
(['foo\nbar'], r"fake('foo\nbar')"),
|
||||
])
|
||||
def test_dbg_signal(stubs, args, expected):
|
||||
assert debug.dbg_signal(stubs.FakeSignal(), args) == expected
|
||||
|
||||
|
||||
class TestGetAllObjects:
|
||||
|
||||
class Object(QObject):
|
||||
|
||||
def __init__(self, name, parent=None):
|
||||
self._name = name
|
||||
super().__init__(parent)
|
||||
|
||||
def __repr__(self):
|
||||
return '<{}>'.format(self._name)
|
||||
|
||||
|
||||
def test_get_all_objects(self, stubs, monkeypatch):
|
||||
# pylint: disable=unused-variable
|
||||
widgets = [self.Object('Widget 1'), self.Object('Widget 2')]
|
||||
app = stubs.FakeQApplication(all_widgets=widgets)
|
||||
monkeypatch.setattr(debug, 'QApplication', app)
|
||||
|
||||
root = QObject()
|
||||
o1 = self.Object('Object 1', root)
|
||||
o2 = self.Object('Object 2', o1)
|
||||
o3 = self.Object('Object 3', root)
|
||||
|
||||
expected = textwrap.dedent("""
|
||||
Qt widgets - 2 objects:
|
||||
<Widget 1>
|
||||
<Widget 2>
|
||||
|
||||
Qt objects - 3 objects:
|
||||
<Object 1>
|
||||
<Object 2>
|
||||
<Object 3>
|
||||
|
||||
global object registry - 0 objects:
|
||||
""").rstrip('\n')
|
||||
|
||||
assert debug.get_all_objects(start_obj=root) == expected
|
||||
|
||||
@pytest.mark.usefixtures('qapp')
|
||||
def test_get_all_objects_qapp(self):
|
||||
objects = debug.get_all_objects()
|
||||
event_dispatcher = '<PyQt5.QtCore.QAbstractEventDispatcher object at'
|
||||
session_manager = '<PyQt5.QtGui.QSessionManager object at'
|
||||
assert event_dispatcher in objects or session_manager in objects
|
@ -22,6 +22,7 @@
|
||||
import os.path
|
||||
|
||||
import pytest
|
||||
import jinja2
|
||||
|
||||
from qutebrowser.utils import jinja
|
||||
|
||||
@ -34,7 +35,7 @@ def patch_read_file(monkeypatch):
|
||||
if path == os.path.join('html', 'test.html'):
|
||||
return """Hello {{var}}"""
|
||||
else:
|
||||
raise ValueError("Invalid path {}!".format(path))
|
||||
raise IOError("Invalid path {}!".format(path))
|
||||
|
||||
monkeypatch.setattr('qutebrowser.utils.jinja.utils.read_file', _read_file)
|
||||
|
||||
@ -47,6 +48,13 @@ def test_simple_template():
|
||||
assert data == "Hello World"
|
||||
|
||||
|
||||
def test_not_found():
|
||||
"""Test with a template which does not exist."""
|
||||
with pytest.raises(jinja2.TemplateNotFound) as excinfo:
|
||||
jinja.env.get_template('does_not_exist.html')
|
||||
assert str(excinfo.value) == 'does_not_exist.html'
|
||||
|
||||
|
||||
def test_utf8():
|
||||
"""Test rendering with an UTF8 template.
|
||||
|
||||
@ -59,3 +67,16 @@ def test_utf8():
|
||||
# https://bitbucket.org/logilab/pylint/issue/490/
|
||||
data = template.render(var='\u2603') # pylint: disable=no-member
|
||||
assert data == "Hello \u2603"
|
||||
|
||||
|
||||
@pytest.mark.parametrize('name, expected', [
|
||||
(None, False),
|
||||
('foo', False),
|
||||
('foo.html', True),
|
||||
('foo.htm', True),
|
||||
('foo.xml', True),
|
||||
('blah/bar/foo.html', True),
|
||||
('foo.bar.html', True),
|
||||
])
|
||||
def test_autoescape(name, expected):
|
||||
assert jinja._guess_autoescape(name) == expected
|
||||
|
@ -17,8 +17,6 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
"""Tests for qutebrowser.utils.log."""
|
||||
|
||||
import logging
|
||||
@ -204,6 +202,7 @@ class TestRAMHandler:
|
||||
assert handler.dump_log() == "Two\nThree"
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
class TestInitLog:
|
||||
|
||||
"""Tests for init_log."""
|
||||
@ -238,8 +237,8 @@ class TestHideQtWarning:
|
||||
|
||||
def test_unfiltered(self, logger, caplog):
|
||||
"""Test a message which is not filtered."""
|
||||
with log.hide_qt_warning("World", logger='qt-tests'):
|
||||
with caplog.atLevel(logging.WARNING, logger='qt-tests'):
|
||||
with log.hide_qt_warning("World", 'qt-tests'):
|
||||
with caplog.atLevel(logging.WARNING, 'qt-tests'):
|
||||
logger.warning("Hello World")
|
||||
assert len(caplog.records()) == 1
|
||||
record = caplog.records()[0]
|
||||
@ -248,21 +247,21 @@ class TestHideQtWarning:
|
||||
|
||||
def test_filtered_exact(self, logger, caplog):
|
||||
"""Test a message which is filtered (exact match)."""
|
||||
with log.hide_qt_warning("Hello", logger='qt-tests'):
|
||||
with caplog.atLevel(logging.WARNING, logger='qt-tests'):
|
||||
with log.hide_qt_warning("Hello", 'qt-tests'):
|
||||
with caplog.atLevel(logging.WARNING, 'qt-tests'):
|
||||
logger.warning("Hello")
|
||||
assert not caplog.records()
|
||||
|
||||
def test_filtered_start(self, logger, caplog):
|
||||
"""Test a message which is filtered (match at line start)."""
|
||||
with log.hide_qt_warning("Hello", logger='qt-tests'):
|
||||
with caplog.atLevel(logging.WARNING, logger='qt-tests'):
|
||||
with log.hide_qt_warning("Hello", 'qt-tests'):
|
||||
with caplog.atLevel(logging.WARNING, 'qt-tests'):
|
||||
logger.warning("Hello World")
|
||||
assert not caplog.records()
|
||||
|
||||
def test_filtered_whitespace(self, logger, caplog):
|
||||
"""Test a message which is filtered (match with whitespace)."""
|
||||
with log.hide_qt_warning("Hello", logger='qt-tests'):
|
||||
with caplog.atLevel(logging.WARNING, logger='qt-tests'):
|
||||
with log.hide_qt_warning("Hello", 'qt-tests'):
|
||||
with caplog.atLevel(logging.WARNING, 'qt-tests'):
|
||||
logger.warning(" Hello World ")
|
||||
assert not caplog.records()
|
||||
|
@ -35,7 +35,6 @@ import unittest
|
||||
import unittest.mock
|
||||
from PyQt5.QtCore import (QDataStream, QPoint, QUrl, QByteArray, QIODevice,
|
||||
QTimer, QBuffer, QFile, QProcess, QFileDevice)
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from qutebrowser import qutebrowser
|
||||
from qutebrowser.utils import qtutils
|
||||
@ -505,19 +504,18 @@ class TestSavefileOpen:
|
||||
|
||||
|
||||
@pytest.mark.parametrize('orgname, expected', [(None, ''), ('test', 'test')])
|
||||
def test_unset_organization(orgname, expected):
|
||||
def test_unset_organization(qapp, orgname, expected):
|
||||
"""Test unset_organization.
|
||||
|
||||
Args:
|
||||
orgname: The organizationName to set initially.
|
||||
expected: The organizationName which is expected when reading back.
|
||||
"""
|
||||
app = QApplication.instance()
|
||||
app.setOrganizationName(orgname)
|
||||
assert app.organizationName() == expected # sanity check
|
||||
qapp.setOrganizationName(orgname)
|
||||
assert qapp.organizationName() == expected # sanity check
|
||||
with qtutils.unset_organization():
|
||||
assert app.organizationName() == ''
|
||||
assert app.organizationName() == expected
|
||||
assert qapp.organizationName() == ''
|
||||
assert qapp.organizationName() == expected
|
||||
|
||||
|
||||
if test_file is not None:
|
||||
@ -921,6 +919,7 @@ class TestPyQIODevice:
|
||||
assert str(excinfo.value) == 'Reading failed'
|
||||
|
||||
|
||||
@pytest.mark.usefixtures('qapp')
|
||||
class TestEventLoop:
|
||||
|
||||
"""Tests for EventLoop.
|
||||
@ -929,8 +928,6 @@ class TestEventLoop:
|
||||
loop: The EventLoop we're testing.
|
||||
"""
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
def _assert_executing(self):
|
||||
"""Slot which gets called from timers to be sure the loop runs."""
|
||||
assert self.loop._executing
|
||||
|
@ -17,8 +17,6 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
"""Tests for qutebrowser.utils.standarddir."""
|
||||
|
||||
import os
|
||||
@ -29,23 +27,22 @@ import logging
|
||||
import textwrap
|
||||
|
||||
from PyQt5.QtCore import QStandardPaths
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
import pytest
|
||||
|
||||
from qutebrowser.utils import standarddir
|
||||
|
||||
|
||||
@pytest.yield_fixture(autouse=True)
|
||||
def change_qapp_name():
|
||||
def change_qapp_name(qapp):
|
||||
"""Change the name of the QApplication instance.
|
||||
|
||||
This changes the applicationName for all tests in this module to
|
||||
"qutebrowser_test".
|
||||
"""
|
||||
old_name = QApplication.instance().applicationName()
|
||||
QApplication.instance().setApplicationName('qutebrowser_test')
|
||||
old_name = qapp.applicationName()
|
||||
qapp.setApplicationName('qutebrowser_test')
|
||||
yield
|
||||
QApplication.instance().setApplicationName(old_name)
|
||||
qapp.setApplicationName(old_name)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -271,7 +268,7 @@ class TestInitCacheDirTag:
|
||||
monkeypatch.setattr('qutebrowser.utils.standarddir.cache',
|
||||
lambda: str(tmpdir))
|
||||
mocker.patch('builtins.open', side_effect=OSError)
|
||||
with caplog.atLevel(logging.ERROR, 'misc'):
|
||||
with caplog.atLevel(logging.ERROR, 'init'):
|
||||
standarddir._init_cachedir_tag()
|
||||
assert len(caplog.records()) == 1
|
||||
assert caplog.records()[0].message == 'Failed to create CACHEDIR.TAG'
|
||||
|
@ -17,8 +17,6 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
"""Tests for qutebrowser.utils.urlutils."""
|
||||
|
||||
import os.path
|
||||
@ -219,7 +217,7 @@ class TestFuzzyUrl:
|
||||
|
||||
@pytest.mark.parametrize('do_search, exception', [
|
||||
(True, qtutils.QtValueError),
|
||||
(False, urlutils.FuzzyUrlError),
|
||||
(False, urlutils.InvalidUrlError),
|
||||
])
|
||||
def test_invalid_url(self, do_search, exception, is_url_mock, monkeypatch):
|
||||
"""Test with an invalid URL."""
|
||||
@ -469,34 +467,40 @@ def test_host_tuple(qurl, tpl):
|
||||
assert urlutils.host_tuple(qurl) == tpl
|
||||
|
||||
|
||||
@pytest.mark.parametrize('url, raising, has_err_string', [
|
||||
(None, False, False),
|
||||
(QUrl(), False, False),
|
||||
(QUrl('http://www.example.com/'), True, False),
|
||||
(QUrl('://'), False, True),
|
||||
])
|
||||
def test_fuzzy_url_error(url, raising, has_err_string):
|
||||
"""Test FuzzyUrlError.
|
||||
class TestInvalidUrlError:
|
||||
|
||||
Args:
|
||||
url: The URL to pass to FuzzyUrlError.
|
||||
raising; True if the FuzzyUrlError should raise itself.
|
||||
has_err_string: Whether the QUrl is expected to have errorString set.
|
||||
"""
|
||||
if raising:
|
||||
expected_exc = ValueError
|
||||
else:
|
||||
expected_exc = urlutils.FuzzyUrlError
|
||||
@pytest.mark.parametrize('url, raising, has_err_string', [
|
||||
(QUrl(), False, False),
|
||||
(QUrl('http://www.example.com/'), True, False),
|
||||
(QUrl('://'), False, True),
|
||||
])
|
||||
def test_invalid_url_error(self, url, raising, has_err_string):
|
||||
"""Test InvalidUrlError.
|
||||
|
||||
with pytest.raises(expected_exc) as excinfo:
|
||||
raise urlutils.FuzzyUrlError("Error message", url)
|
||||
|
||||
if not raising:
|
||||
if has_err_string:
|
||||
expected_text = "Error message: " + url.errorString()
|
||||
Args:
|
||||
url: The URL to pass to InvalidUrlError.
|
||||
raising; True if the InvalidUrlError should raise itself.
|
||||
has_err_string: Whether the QUrl is expected to have errorString
|
||||
set.
|
||||
"""
|
||||
if raising:
|
||||
expected_exc = ValueError
|
||||
else:
|
||||
expected_text = "Error message"
|
||||
assert str(excinfo.value) == expected_text
|
||||
expected_exc = urlutils.InvalidUrlError
|
||||
|
||||
with pytest.raises(expected_exc) as excinfo:
|
||||
raise urlutils.InvalidUrlError(url)
|
||||
|
||||
if not raising:
|
||||
expected_text = "Invalid URL"
|
||||
if has_err_string:
|
||||
expected_text += " - " + url.errorString()
|
||||
assert str(excinfo.value) == expected_text
|
||||
|
||||
def test_value_error_subclass(self):
|
||||
"""Make sure InvalidUrlError is a ValueError subclass."""
|
||||
with pytest.raises(ValueError):
|
||||
raise urlutils.InvalidUrlError(QUrl())
|
||||
|
||||
|
||||
@pytest.mark.parametrize('are_same, url1, url2', [
|
||||
@ -522,5 +526,72 @@ def test_same_domain(are_same, url1, url2):
|
||||
])
|
||||
def test_same_domain_invalid_url(url1, url2):
|
||||
"""Test same_domain with invalid URLs."""
|
||||
with pytest.raises(ValueError):
|
||||
with pytest.raises(urlutils.InvalidUrlError):
|
||||
urlutils.same_domain(QUrl(url1), QUrl(url2))
|
||||
|
||||
class TestIncDecNumber:
|
||||
|
||||
"""Tests for urlutils.incdec_number()."""
|
||||
|
||||
@pytest.mark.parametrize('url, incdec, output', [
|
||||
("http://example.com/index1.html", "increment", "http://example.com/index2.html"),
|
||||
("http://foo.bar/folder_1/image_2", "increment", "http://foo.bar/folder_1/image_3"),
|
||||
("http://bbc.c0.uk:80/story_1", "increment", "http://bbc.c0.uk:80/story_2"),
|
||||
("http://mydomain.tld/1_%C3%A4", "increment", "http://mydomain.tld/2_%C3%A4"),
|
||||
("http://example.com/site/5#5", "increment", "http://example.com/site/6#5"),
|
||||
|
||||
("http://example.com/index10.html", "decrement", "http://example.com/index9.html"),
|
||||
("http://foo.bar/folder_1/image_3", "decrement", "http://foo.bar/folder_1/image_2"),
|
||||
("http://bbc.c0.uk:80/story_1", "decrement", "http://bbc.c0.uk:80/story_0"),
|
||||
("http://mydomain.tld/2_%C3%A4", "decrement", "http://mydomain.tld/1_%C3%A4"),
|
||||
("http://example.com/site/5#5", "decrement", "http://example.com/site/4#5"),
|
||||
])
|
||||
def test_incdec_number(self, url, incdec, output):
|
||||
"""Test incdec_number with valid URLs."""
|
||||
new_url = urlutils.incdec_number(QUrl(url), incdec)
|
||||
assert new_url == QUrl(output)
|
||||
|
||||
@pytest.mark.parametrize('url', [
|
||||
"http://example.com/long/path/but/no/number",
|
||||
"http://ex4mple.com/number/in/hostname",
|
||||
"http://example.com:42/number/in/port",
|
||||
"http://www2.example.com/number/in/subdomain",
|
||||
"http://example.com/%C3%B6/urlencoded/data",
|
||||
"http://example.com/number/in/anchor#5",
|
||||
"http://www2.ex4mple.com:42/all/of/the/%C3%A4bove#5",
|
||||
])
|
||||
def test_no_number(self, url):
|
||||
"""Test incdec_number with URLs that don't contain a number."""
|
||||
with pytest.raises(urlutils.IncDecError):
|
||||
urlutils.incdec_number(QUrl(url), "increment")
|
||||
|
||||
def test_number_below_0(self):
|
||||
"""Test incdec_number with a number that would be below zero
|
||||
after decrementing."""
|
||||
with pytest.raises(urlutils.IncDecError):
|
||||
urlutils.incdec_number(QUrl('http://example.com/page_0.html'),
|
||||
'decrement')
|
||||
|
||||
def test_invalid_url(self):
|
||||
"""Test if incdec_number rejects an invalid URL."""
|
||||
with pytest.raises(urlutils.InvalidUrlError):
|
||||
urlutils.incdec_number(QUrl(""), "increment")
|
||||
|
||||
def test_wrong_mode(self):
|
||||
"""Test if incdec_number rejects a wrong parameter for the incdec
|
||||
argument."""
|
||||
valid_url = QUrl("http://example.com/0")
|
||||
with pytest.raises(ValueError):
|
||||
urlutils.incdec_number(valid_url, "foobar")
|
||||
|
||||
@pytest.mark.parametrize("url, msg, expected_str", [
|
||||
("http://example.com", "Invalid", "Invalid: http://example.com"),
|
||||
])
|
||||
def test_incdec_error(self, url, msg, expected_str):
|
||||
"""Test IncDecError."""
|
||||
url = QUrl(url)
|
||||
with pytest.raises(urlutils.IncDecError) as excinfo:
|
||||
raise urlutils.IncDecError(msg, url)
|
||||
|
||||
assert excinfo.value.url == url
|
||||
assert str(excinfo.value) == expected_str
|
||||
|
@ -17,8 +17,6 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
"""Tests for qutebrowser.utils.version."""
|
||||
|
||||
import io
|
||||
@ -105,12 +103,13 @@ class TestGitStr:
|
||||
commit_file_mock.return_value = 'deadbeef'
|
||||
assert version._git_str() == 'deadbeef'
|
||||
|
||||
def test_frozen_oserror(self, commit_file_mock, monkeypatch):
|
||||
def test_frozen_oserror(self, caplog, commit_file_mock, monkeypatch):
|
||||
"""Test with sys.frozen=True and OSError when reading git-commit-id."""
|
||||
monkeypatch.setattr(qutebrowser.utils.version.sys, 'frozen', True,
|
||||
raising=False)
|
||||
commit_file_mock.side_effect = OSError
|
||||
assert version._git_str() is None
|
||||
with caplog.atLevel(logging.ERROR, 'misc'):
|
||||
assert version._git_str() is None
|
||||
|
||||
@pytest.mark.not_frozen
|
||||
def test_normal_successful(self, git_str_subprocess_fake):
|
||||
@ -130,13 +129,15 @@ class TestGitStr:
|
||||
commit_file_mock.return_value = '1b4d1dea'
|
||||
assert version._git_str() == '1b4d1dea'
|
||||
|
||||
def test_normal_path_oserror(self, mocker, git_str_subprocess_fake):
|
||||
def test_normal_path_oserror(self, mocker, git_str_subprocess_fake,
|
||||
caplog):
|
||||
"""Test with things raising OSError."""
|
||||
m = mocker.patch('qutebrowser.utils.version.os')
|
||||
m.path.join.side_effect = OSError
|
||||
mocker.patch('qutebrowser.utils.version.utils.read_file',
|
||||
side_effect=OSError)
|
||||
assert version._git_str() is None
|
||||
with caplog.atLevel(logging.ERROR, 'misc'):
|
||||
assert version._git_str() is None
|
||||
|
||||
@pytest.mark.not_frozen
|
||||
def test_normal_path_nofile(self, monkeypatch, caplog,
|
||||
|
@ -17,8 +17,6 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
"""Tests for the NeighborList class."""
|
||||
|
||||
from qutebrowser.utils import usertypes
|
||||
|
@ -86,6 +86,6 @@ def test_abort_typeerror(question, qtbot, mocker, caplog):
|
||||
"""Test Question.abort() with .emit() raising a TypeError."""
|
||||
signal_mock = mocker.patch('qutebrowser.utils.usertypes.Question.aborted')
|
||||
signal_mock.emit.side_effect = TypeError
|
||||
with caplog.atLevel(logging.ERROR):
|
||||
with caplog.atLevel(logging.ERROR, 'misc'):
|
||||
question.abort()
|
||||
assert caplog.records()[0].message == 'Error while aborting question'
|
||||
|
@ -17,8 +17,6 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# pylint: disable=protected-access
|
||||
|
||||
"""Tests for Timer."""
|
||||
|
||||
from qutebrowser.utils import usertypes
|
||||
@ -74,13 +72,13 @@ def test_start_overflow():
|
||||
def test_timeout_start(qtbot):
|
||||
"""Make sure the timer works with start()."""
|
||||
t = usertypes.Timer()
|
||||
with qtbot.waitSignal(t.timeout, raising=True):
|
||||
with qtbot.waitSignal(t.timeout, timeout=3000, raising=True):
|
||||
t.start(200)
|
||||
|
||||
|
||||
def test_timeout_set_interval(qtbot):
|
||||
"""Make sure the timer works with setInterval()."""
|
||||
t = usertypes.Timer()
|
||||
with qtbot.waitSignal(t.timeout, raising=True):
|
||||
with qtbot.waitSignal(t.timeout, timeout=3000, raising=True):
|
||||
t.setInterval(200)
|
||||
t.start()
|
||||
|
91
tox.ini
91
tox.ini
@ -4,23 +4,14 @@
|
||||
# and then run "tox" from this directory.
|
||||
|
||||
[tox]
|
||||
envlist = smoke,unittests,misc,pep257,pyflakes,pep8,mccabe,pylint,pyroma,check-manifest
|
||||
envlist = py34,misc,pep257,pyflakes,pep8,mccabe,pylint,pyroma,check-manifest
|
||||
|
||||
[testenv]
|
||||
passenv = PYTHON
|
||||
basepython = python3
|
||||
|
||||
[testenv:mkvenv]
|
||||
commands = {envpython} scripts/link_pyqt.py --tox {envdir}
|
||||
envdir = {toxinidir}/.venv
|
||||
usedevelop = true
|
||||
|
||||
[testenv:unittests]
|
||||
# https://bitbucket.org/hpk42/tox/issue/246/ - only needed for Windows though
|
||||
setenv =
|
||||
QT_QPA_PLATFORM_PLUGIN_PATH={envdir}/Lib/site-packages/PyQt5/plugins/platforms
|
||||
PYTEST_QT_API=pyqt5
|
||||
passenv = PYTHON DISPLAY XAUTHORITY HOME
|
||||
passenv = PYTHON DISPLAY XAUTHORITY HOME USERNAME USER CI
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
py==1.4.30
|
||||
@ -28,18 +19,29 @@ deps =
|
||||
pytest-capturelog==0.7
|
||||
pytest-qt==1.5.1
|
||||
pytest-mock==0.7.0
|
||||
pytest-html==1.3.2
|
||||
pytest-html==1.4
|
||||
hypothesis==1.10.1
|
||||
hypothesis-pytest==0.15.1
|
||||
hypothesis-pytest==0.17.0
|
||||
coverage==3.7.1
|
||||
pytest-cov==2.0.0
|
||||
cov-core==1.15.0
|
||||
commands =
|
||||
{envpython} scripts/link_pyqt.py --tox {envdir}
|
||||
{envpython} -m py.test --strict -rfEsw {posargs:tests}
|
||||
{envpython} -m py.test --strict -rfEsw --cov qutebrowser --cov-report xml --cov-report= {posargs:tests}
|
||||
{envpython} scripts/dev/check_coverage.py {posargs}
|
||||
{envpython} -m qutebrowser --no-err-windows --nowindow --temp-basedir about:blank ":later 500 quit"
|
||||
|
||||
[testenv:mkvenv]
|
||||
basepython = python3
|
||||
commands = {envpython} scripts/link_pyqt.py --tox {envdir}
|
||||
envdir = {toxinidir}/.venv
|
||||
usedevelop = true
|
||||
|
||||
[testenv:unittests-watch]
|
||||
setenv = {[testenv:unittests]setenv}
|
||||
passenv = {[testenv:unittests]passenv}
|
||||
basepython = python3
|
||||
passenv = {[testenv]passenv}
|
||||
deps =
|
||||
{[testenv:unittests]deps}
|
||||
{[testenv]deps}
|
||||
pytest-testmon==0.6
|
||||
pytest-watch==3.2.0
|
||||
commands =
|
||||
@ -47,38 +49,32 @@ commands =
|
||||
{envdir}/bin/ptw -- --testmon --strict -rfEsw {posargs:tests}
|
||||
|
||||
[testenv:unittests-frozen]
|
||||
setenv = {[testenv:unittests]setenv}
|
||||
passenv = {[testenv:unittests]passenv}
|
||||
basepython = python3
|
||||
passenv = {[testenv]passenv}
|
||||
skip_install = true
|
||||
deps =
|
||||
{[testenv:unittests]deps}
|
||||
{[testenv]deps}
|
||||
cx_Freeze==4.3.4
|
||||
commands =
|
||||
{envpython} scripts/link_pyqt.py --tox {envdir}
|
||||
{envpython} scripts/dev/freeze_tests.py build_exe -b {envdir}/build
|
||||
{envdir}/build/run-frozen-tests --strict -rfEsw {posargs}
|
||||
|
||||
[testenv:coverage]
|
||||
passenv = PYTHON DISPLAY XAUTHORITY HOME
|
||||
deps =
|
||||
{[testenv:unittests]deps}
|
||||
coverage==3.7.1
|
||||
pytest-cov==2.0.0
|
||||
cov-core==1.15.0
|
||||
commands =
|
||||
{envpython} scripts/link_pyqt.py --tox {envdir}
|
||||
{envpython} -m py.test --strict -rfEswx -v --cov qutebrowser --cov-report term --cov-report html --cov-report xml {posargs:tests}
|
||||
{envpython} scripts/dev/check_coverage.py
|
||||
|
||||
[testenv:misc]
|
||||
basepython = python3
|
||||
# For global .gitignore files
|
||||
passenv = HOME
|
||||
deps =
|
||||
commands =
|
||||
{envpython} scripts/dev/misc_checks.py git
|
||||
{envpython} scripts/dev/misc_checks.py vcs
|
||||
{envpython} scripts/dev/misc_checks.py spelling
|
||||
|
||||
[testenv:pylint]
|
||||
basepython = python3
|
||||
skip_install = true
|
||||
setenv = PYTHONPATH={toxinidir}/scripts/dev
|
||||
passenv =
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
astroid==1.3.8
|
||||
@ -93,9 +89,10 @@ commands =
|
||||
{envpython} scripts/dev/run_pylint_on_tests.py --rcfile=.pylintrc --output-format=colorized --reports=no --expected-line-ending-format=LF
|
||||
|
||||
[testenv:pep257]
|
||||
basepython = python3
|
||||
skip_install = true
|
||||
deps = pep257==0.6.0
|
||||
passenv = PYTHON LANG
|
||||
deps = pep257==0.6.0
|
||||
# Disabled checks:
|
||||
# D102: Missing docstring in public method (will be handled by others)
|
||||
# D103: Missing docstring in public function (will be handled by others)
|
||||
@ -104,8 +101,10 @@ passenv = PYTHON LANG
|
||||
commands = {envpython} -m pep257 scripts tests qutebrowser --ignore=D102,D103,D209,D402 '--match=(?!resources|test_*).*\.py'
|
||||
|
||||
[testenv:pyflakes]
|
||||
basepython = python3
|
||||
# https://github.com/fschulze/pytest-flakes/issues/6
|
||||
setenv = LANG=en_US.UTF-8
|
||||
passenv =
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
py==1.4.30
|
||||
@ -117,6 +116,8 @@ commands =
|
||||
{envpython} -m py.test -q --flakes --ignore=tests
|
||||
|
||||
[testenv:pep8]
|
||||
basepython = python3
|
||||
passenv =
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
py==1.4.30
|
||||
@ -128,6 +129,8 @@ commands =
|
||||
{envpython} -m py.test -q --pep8 --ignore=tests
|
||||
|
||||
[testenv:mccabe]
|
||||
basepython = python3
|
||||
passenv =
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
py==1.4.30
|
||||
@ -139,7 +142,9 @@ commands =
|
||||
{envpython} -m py.test -q --mccabe --ignore=tests
|
||||
|
||||
[testenv:pyroma]
|
||||
basepython = python3
|
||||
skip_install = true
|
||||
passenv =
|
||||
deps =
|
||||
pyroma==1.8.2
|
||||
docutils==0.12
|
||||
@ -148,7 +153,9 @@ commands =
|
||||
{envdir}/bin/pyroma .
|
||||
|
||||
[testenv:check-manifest]
|
||||
basepython = python3
|
||||
skip_install = true
|
||||
passenv =
|
||||
deps =
|
||||
check-manifest==0.25
|
||||
commands =
|
||||
@ -156,8 +163,10 @@ commands =
|
||||
{envdir}/bin/check-manifest --ignore 'qutebrowser/git-commit-id,qutebrowser/html/doc,qutebrowser/html/doc/*,*/__pycache__'
|
||||
|
||||
[testenv:docs]
|
||||
basepython = python3
|
||||
skip_install = true
|
||||
whitelist_externals = git
|
||||
passenv =
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
commands =
|
||||
@ -166,22 +175,12 @@ commands =
|
||||
git --no-pager diff --exit-code --stat
|
||||
{envpython} scripts/asciidoc2html.py {posargs}
|
||||
|
||||
[testenv:smoke]
|
||||
# https://bitbucket.org/hpk42/tox/issue/246/ - only needed for Windows though
|
||||
setenv = QT_QPA_PLATFORM_PLUGIN_PATH={envdir}/Lib/site-packages/PyQt5/plugins/platforms
|
||||
passenv = PYTHON DISPLAY XAUTHORITY HOME USERNAME USER
|
||||
deps =
|
||||
-r{toxinidir}/requirements.txt
|
||||
commands =
|
||||
{envpython} scripts/link_pyqt.py --tox {envdir}
|
||||
{envpython} -m qutebrowser --no-err-windows --nowindow --temp-basedir about:blank ":later 500 quit"
|
||||
|
||||
[testenv:smoke-frozen]
|
||||
setenv = {[testenv:smoke]setenv}
|
||||
passenv = {[testenv:smoke]passenv}
|
||||
basepython = python3
|
||||
passenv = {[testenv]passenv}
|
||||
skip_install = true
|
||||
deps =
|
||||
{[testenv:smoke]deps}
|
||||
{[testenv]deps}
|
||||
cx_Freeze==4.3.4
|
||||
commands =
|
||||
{envpython} scripts/link_pyqt.py --tox {envdir}
|
||||
|
Loading…
Reference in New Issue
Block a user