commit
07552dddfe
@ -127,7 +127,7 @@ def split(s, keep=False):
|
|||||||
"""Split a string via ShellLexer.
|
"""Split a string via ShellLexer.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
keep: Whether to keep are special chars in the split output.
|
keep: Whether to keep special chars in the split output.
|
||||||
"""
|
"""
|
||||||
lexer = ShellLexer(s)
|
lexer = ShellLexer(s)
|
||||||
lexer.keep = keep
|
lexer.keep = keep
|
||||||
|
44
tests/commands/test_runners.py
Normal file
44
tests/commands/test_runners.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# 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 qutebrowser.commands.runners."""
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from qutebrowser.commands import runners, cmdexc
|
||||||
|
|
||||||
|
|
||||||
|
class TestCommandRunner:
|
||||||
|
|
||||||
|
"""Tests for CommandRunner."""
|
||||||
|
|
||||||
|
def test_parse_all(self, cmdline_test):
|
||||||
|
"""Test parsing of commands.
|
||||||
|
|
||||||
|
See https://github.com/The-Compiler/qutebrowser/issues/615
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cmdline_test: A pytest fixture which provides testcases.
|
||||||
|
"""
|
||||||
|
cr = runners.CommandRunner(0)
|
||||||
|
if cmdline_test.valid:
|
||||||
|
list(cr.parse_all(cmdline_test.cmd, aliases=False))
|
||||||
|
else:
|
||||||
|
with pytest.raises(cmdexc.NoSuchCommandError):
|
||||||
|
list(cr.parse_all(cmdline_test.cmd, aliases=False))
|
@ -31,7 +31,9 @@ from PyQt5.QtCore import QObject
|
|||||||
from PyQt5.QtGui import QColor
|
from PyQt5.QtGui import QColor
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from qutebrowser.config import config, configexc
|
from qutebrowser.config import config, configexc, configdata
|
||||||
|
from qutebrowser.config.parsers import keyconf
|
||||||
|
from qutebrowser.commands import runners
|
||||||
from qutebrowser.utils import objreg, standarddir
|
from qutebrowser.utils import objreg, standarddir
|
||||||
|
|
||||||
|
|
||||||
@ -155,6 +157,27 @@ class TestConfigParser:
|
|||||||
self.cfg.get('general', 'bar') # pylint: disable=bad-config-call
|
self.cfg.get('general', 'bar') # pylint: disable=bad-config-call
|
||||||
|
|
||||||
|
|
||||||
|
class TestKeyConfigParser:
|
||||||
|
|
||||||
|
"""Test config.parsers.keyconf.KeyConfigParser."""
|
||||||
|
|
||||||
|
def test_cmd_binding(self, cmdline_test):
|
||||||
|
"""Test various command bindings.
|
||||||
|
|
||||||
|
See https://github.com/The-Compiler/qutebrowser/issues/615
|
||||||
|
|
||||||
|
Args:
|
||||||
|
cmdline_test: A pytest fixture which provides testcases.
|
||||||
|
"""
|
||||||
|
kcp = keyconf.KeyConfigParser(None, None)
|
||||||
|
kcp._cur_section = 'normal'
|
||||||
|
if cmdline_test.valid:
|
||||||
|
kcp._read_command(cmdline_test.cmd)
|
||||||
|
else:
|
||||||
|
with pytest.raises(keyconf.KeyConfigError):
|
||||||
|
kcp._read_command(cmdline_test.cmd)
|
||||||
|
|
||||||
|
|
||||||
class TestDefaultConfig:
|
class TestDefaultConfig:
|
||||||
|
|
||||||
"""Test validating of the default config."""
|
"""Test validating of the default config."""
|
||||||
@ -164,6 +187,16 @@ class TestDefaultConfig:
|
|||||||
conf = config.ConfigManager(None, None)
|
conf = config.ConfigManager(None, None)
|
||||||
conf._validate_all()
|
conf._validate_all()
|
||||||
|
|
||||||
|
def test_default_key_config(self):
|
||||||
|
"""Test validating of the default key config."""
|
||||||
|
# We import qutebrowser.app so the cmdutils.register decorators run.
|
||||||
|
import qutebrowser.app # pylint: disable=unused-variable
|
||||||
|
conf = keyconf.KeyConfigParser(None, None)
|
||||||
|
runner = runners.CommandRunner(win_id=0)
|
||||||
|
for sectname in configdata.KEY_DATA:
|
||||||
|
for cmd in conf.get_bindings_for(sectname).values():
|
||||||
|
runner.parse(cmd, aliases=False)
|
||||||
|
|
||||||
|
|
||||||
class TestConfigInit:
|
class TestConfigInit:
|
||||||
|
|
||||||
|
@ -19,6 +19,9 @@
|
|||||||
|
|
||||||
"""The qutebrowser test suite contest file."""
|
"""The qutebrowser test suite contest file."""
|
||||||
|
|
||||||
|
import collections
|
||||||
|
import itertools
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
@ -49,15 +52,23 @@ def unicode_encode_err():
|
|||||||
'fake exception') # reason
|
'fake exception') # reason
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope='session')
|
||||||
|
def qnam():
|
||||||
|
"""Session-wide QNetworkAccessManager."""
|
||||||
|
from PyQt5.QtNetwork import QNetworkAccessManager
|
||||||
|
nam = QNetworkAccessManager()
|
||||||
|
nam.setNetworkAccessible(QNetworkAccessManager.NotAccessible)
|
||||||
|
return nam
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def webpage():
|
def webpage(qnam):
|
||||||
"""Get a new QWebPage object."""
|
"""Get a new QWebPage object."""
|
||||||
from PyQt5.QtWebKitWidgets import QWebPage
|
from PyQt5.QtWebKitWidgets import QWebPage
|
||||||
from PyQt5.QtNetwork import QNetworkAccessManager
|
|
||||||
|
|
||||||
page = QWebPage()
|
page = QWebPage()
|
||||||
nam = page.networkAccessManager()
|
page.networkAccessManager().deleteLater()
|
||||||
nam.setNetworkAccessible(QNetworkAccessManager.NotAccessible)
|
page.setNetworkAccessManager(qnam)
|
||||||
return page
|
return page
|
||||||
|
|
||||||
|
|
||||||
@ -76,3 +87,63 @@ def fake_keyevent_factory():
|
|||||||
return evtmock
|
return evtmock
|
||||||
|
|
||||||
return fake_keyevent
|
return fake_keyevent
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_collection_modifyitems(items):
|
||||||
|
"""Automatically add a 'gui' marker to all gui-related tests.
|
||||||
|
|
||||||
|
pytest hook called after collection has been performed, adds a marker
|
||||||
|
named "gui" which can be used to filter gui tests from the command line.
|
||||||
|
For example:
|
||||||
|
|
||||||
|
py.test -m "not gui" # run all tests except gui tests
|
||||||
|
py.test -m "gui" # run only gui tests
|
||||||
|
|
||||||
|
Args:
|
||||||
|
items: list of _pytest.main.Node items, where each item represents
|
||||||
|
a python test that will be executed.
|
||||||
|
|
||||||
|
Reference:
|
||||||
|
http://pytest.org/latest/plugins.html
|
||||||
|
"""
|
||||||
|
for item in items:
|
||||||
|
if 'qtbot' in getattr(item, 'fixturenames', ()):
|
||||||
|
item.add_marker('gui')
|
||||||
|
|
||||||
|
|
||||||
|
def _generate_cmdline_tests():
|
||||||
|
"""Generate testcases for test_split_binding."""
|
||||||
|
# pylint: disable=invalid-name
|
||||||
|
TestCase = collections.namedtuple('TestCase', 'cmd, valid')
|
||||||
|
separators = [';;', ' ;; ', ';; ', ' ;;']
|
||||||
|
invalid = ['foo', '']
|
||||||
|
valid = ['leave-mode', 'hint all']
|
||||||
|
# Valid command only -> valid
|
||||||
|
for item in valid:
|
||||||
|
yield TestCase(''.join(item), True)
|
||||||
|
# Invalid command only -> invalid
|
||||||
|
for item in valid:
|
||||||
|
yield TestCase(''.join(item), True)
|
||||||
|
# Invalid command combined with invalid command -> invalid
|
||||||
|
for item in itertools.product(invalid, separators, invalid):
|
||||||
|
yield TestCase(''.join(item), False)
|
||||||
|
# Valid command combined with valid command -> valid
|
||||||
|
for item in itertools.product(valid, separators, valid):
|
||||||
|
yield TestCase(''.join(item), True)
|
||||||
|
# Valid command combined with invalid command -> invalid
|
||||||
|
for item in itertools.product(valid, separators, invalid):
|
||||||
|
yield TestCase(''.join(item), False)
|
||||||
|
# Invalid command combined with valid command -> invalid
|
||||||
|
for item in itertools.product(invalid, separators, valid):
|
||||||
|
yield TestCase(''.join(item), False)
|
||||||
|
# Command with no_cmd_split combined with an "invalid" command -> valid
|
||||||
|
for item in itertools.product(['bind x open'], separators, invalid):
|
||||||
|
yield TestCase(''.join(item), True)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(params=_generate_cmdline_tests())
|
||||||
|
def cmdline_test(request):
|
||||||
|
"""Fixture which generates tests for things validating commandlines."""
|
||||||
|
# Import qutebrowser.app so all cmdutils.register decorators get run.
|
||||||
|
import qutebrowser.app # pylint: disable=unused-variable
|
||||||
|
return request.param
|
||||||
|
@ -63,44 +63,27 @@ class TestSplitCount:
|
|||||||
|
|
||||||
"""Test the _split_count method.
|
"""Test the _split_count method.
|
||||||
|
|
||||||
Attributes:
|
Class Attributes:
|
||||||
kp: The BaseKeyParser we're testing.
|
TESTS: list of parameters for the tests, as tuples of
|
||||||
|
(input_key, supports_count, expected)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
TESTS = [
|
||||||
def setup(self):
|
# (input_key, supports_count, expected)
|
||||||
self.kp = basekeyparser.BaseKeyParser(0, supports_count=True)
|
('10', True, (10, '')),
|
||||||
|
('10foo', True, (10, 'foo')),
|
||||||
|
('-1foo', True, (None, '-1foo')),
|
||||||
|
('10e4foo', True, (10, 'e4foo')),
|
||||||
|
('foo', True, (None, 'foo')),
|
||||||
|
('10foo', False, (None, '10foo')),
|
||||||
|
]
|
||||||
|
|
||||||
def test_onlycount(self):
|
@pytest.mark.parametrize('input_key, supports_count, expected', TESTS)
|
||||||
|
def test_splitcount(self, input_key, supports_count, expected):
|
||||||
"""Test split_count with only a count."""
|
"""Test split_count with only a count."""
|
||||||
self.kp._keystring = '10'
|
kp = basekeyparser.BaseKeyParser(0, supports_count=supports_count)
|
||||||
assert self.kp._split_count() == (10, '')
|
kp._keystring = input_key
|
||||||
|
assert kp._split_count() == expected
|
||||||
def test_normalcount(self):
|
|
||||||
"""Test split_count with count and text."""
|
|
||||||
self.kp._keystring = '10foo'
|
|
||||||
assert self.kp._split_count() == (10, 'foo')
|
|
||||||
|
|
||||||
def test_minuscount(self):
|
|
||||||
"""Test split_count with a negative count."""
|
|
||||||
self.kp._keystring = '-1foo'
|
|
||||||
assert self.kp._split_count() == (None, '-1foo')
|
|
||||||
|
|
||||||
def test_expcount(self):
|
|
||||||
"""Test split_count with an exponential count."""
|
|
||||||
self.kp._keystring = '10e4foo'
|
|
||||||
assert self.kp._split_count() == (10, 'e4foo')
|
|
||||||
|
|
||||||
def test_nocount(self):
|
|
||||||
"""Test split_count with only a command."""
|
|
||||||
self.kp._keystring = 'foo'
|
|
||||||
assert self.kp._split_count() == (None, 'foo')
|
|
||||||
|
|
||||||
def test_nosupport(self):
|
|
||||||
"""Test split_count with a count when counts aren't supported."""
|
|
||||||
self.kp._supports_count = False
|
|
||||||
self.kp._keystring = '10foo'
|
|
||||||
assert self.kp._split_count() == (None, '10foo')
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.usefixtures('fake_keyconfig', 'mock_timer')
|
@pytest.mark.usefixtures('fake_keyconfig', 'mock_timer')
|
||||||
|
90
tests/misc/test_miscwidgets.py
Normal file
90
tests/misc/test_miscwidgets.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||||
|
|
||||||
|
# Copyright 2014-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/>.
|
||||||
|
|
||||||
|
"""Test widgets in miscwidgets module."""
|
||||||
|
|
||||||
|
from unittest import mock
|
||||||
|
from PyQt5.QtCore import Qt
|
||||||
|
from PyQt5.QtWidgets import QApplication
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from qutebrowser.misc.miscwidgets import CommandLineEdit
|
||||||
|
|
||||||
|
|
||||||
|
class TestCommandLineEdit:
|
||||||
|
|
||||||
|
"""Tests for CommandLineEdit widget."""
|
||||||
|
|
||||||
|
@pytest.yield_fixture
|
||||||
|
def cmd_edit(self, qtbot):
|
||||||
|
"""Fixture to initialize a CommandLineEdit."""
|
||||||
|
cmd_edit = CommandLineEdit(None)
|
||||||
|
cmd_edit.set_prompt(':')
|
||||||
|
qtbot.add_widget(cmd_edit)
|
||||||
|
assert cmd_edit.text() == ''
|
||||||
|
yield cmd_edit
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_clipboard(self, mocker):
|
||||||
|
"""Fixture to mock QApplication.clipboard.
|
||||||
|
|
||||||
|
Return:
|
||||||
|
The mocked QClipboard object.
|
||||||
|
"""
|
||||||
|
mocker.patch.object(QApplication, 'clipboard')
|
||||||
|
clipboard = mock.MagicMock()
|
||||||
|
clipboard.supportsSelection.return_value = True
|
||||||
|
QApplication.clipboard.return_value = clipboard
|
||||||
|
return clipboard
|
||||||
|
|
||||||
|
def test_position(self, qtbot, cmd_edit):
|
||||||
|
"""Test cursor position based on the prompt."""
|
||||||
|
qtbot.keyClicks(cmd_edit, ':hello')
|
||||||
|
assert cmd_edit.text() == ':hello'
|
||||||
|
assert cmd_edit.cursorPosition() == len(':hello')
|
||||||
|
|
||||||
|
cmd_edit.home(mark=True)
|
||||||
|
assert cmd_edit.cursorPosition() == len(':hello')
|
||||||
|
qtbot.keyClick(cmd_edit, Qt.Key_Delete)
|
||||||
|
assert cmd_edit.text() == ':'
|
||||||
|
qtbot.keyClick(cmd_edit, Qt.Key_Backspace)
|
||||||
|
assert cmd_edit.text() == ':'
|
||||||
|
|
||||||
|
qtbot.keyClicks(cmd_edit, 'hey again')
|
||||||
|
assert cmd_edit.text() == ':hey again'
|
||||||
|
|
||||||
|
def test_invalid_prompt(self, qtbot, cmd_edit):
|
||||||
|
"""Test preventing of an invalid prompt being entered."""
|
||||||
|
qtbot.keyClicks(cmd_edit, '$hello')
|
||||||
|
assert cmd_edit.text() == ''
|
||||||
|
|
||||||
|
def test_clipboard_paste(self, qtbot, cmd_edit, mock_clipboard):
|
||||||
|
"""Test pasting commands from clipboard."""
|
||||||
|
mock_clipboard.text.return_value = ':command'
|
||||||
|
qtbot.keyClick(cmd_edit, Qt.Key_Insert, Qt.ShiftModifier)
|
||||||
|
assert cmd_edit.text() == ':command'
|
||||||
|
|
||||||
|
mock_clipboard.text.return_value = ' param1'
|
||||||
|
qtbot.keyClick(cmd_edit, Qt.Key_Insert, Qt.ShiftModifier)
|
||||||
|
assert cmd_edit.text() == ':command param1'
|
||||||
|
|
||||||
|
cmd_edit.clear()
|
||||||
|
mock_clipboard.text.return_value = '$ command'
|
||||||
|
qtbot.keyClick(cmd_edit, Qt.Key_Insert, Qt.ShiftModifier)
|
||||||
|
assert cmd_edit.text() == ':command param1'
|
@ -18,8 +18,9 @@
|
|||||||
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
"""Tests for qutebrowser.misc.split."""
|
"""Tests for qutebrowser.misc.split."""
|
||||||
|
import collections
|
||||||
|
|
||||||
import unittest
|
import pytest
|
||||||
|
|
||||||
from qutebrowser.misc import split
|
from qutebrowser.misc import split
|
||||||
|
|
||||||
@ -29,7 +30,7 @@ from qutebrowser.misc import split
|
|||||||
|
|
||||||
# Format: input/split|output|without|keep/split|output|with|keep/
|
# Format: input/split|output|without|keep/split|output|with|keep/
|
||||||
|
|
||||||
test_data = r"""
|
test_data_str = r"""
|
||||||
one two/one|two/one| two/
|
one two/one|two/one| two/
|
||||||
one "two three" four/one|two three|four/one| "two three"| four/
|
one "two three" four/one|two three|four/one| "two three"| four/
|
||||||
one 'two three' four/one|two three|four/one| 'two three'| four/
|
one 'two three' four/one|two three|four/one| 'two three'| four/
|
||||||
@ -104,36 +105,56 @@ foo\ bar/foo bar/foo\ bar/
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class SplitTests(unittest.TestCase):
|
def _parse_split_test_data_str():
|
||||||
|
"""
|
||||||
|
Parse the test data set into a namedtuple to use in tests.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of namedtuples with str attributes: input, keep, no_keep
|
||||||
|
"""
|
||||||
|
tuple_class = collections.namedtuple('TestCase', 'input, keep, no_keep')
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for line in test_data_str.splitlines():
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
data = line.split('/')
|
||||||
|
item = tuple_class(input=data[0], keep=data[1].split('|'),
|
||||||
|
no_keep=data[2].split('|'))
|
||||||
|
result.append(item)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
class TestSplit:
|
||||||
|
|
||||||
"""Test split."""
|
"""Test split."""
|
||||||
|
|
||||||
def test_split(self):
|
@pytest.fixture(params=_parse_split_test_data_str())
|
||||||
|
def split_test_case(self, request):
|
||||||
|
"""Fixture to automatically parametrize all depending tests.
|
||||||
|
|
||||||
|
It will use the test data from test_data_str, parsed using
|
||||||
|
_parse_split_test_data_str().
|
||||||
|
"""
|
||||||
|
return request.param
|
||||||
|
|
||||||
|
def test_split(self, split_test_case):
|
||||||
"""Test splitting."""
|
"""Test splitting."""
|
||||||
for case in test_data.strip().splitlines():
|
items = split.split(split_test_case.input)
|
||||||
cmd, out = case.split('/')[:-2]
|
assert items == split_test_case.keep
|
||||||
with self.subTest(cmd=cmd):
|
|
||||||
items = split.split(cmd)
|
|
||||||
self.assertEqual(items, out.split('|'))
|
|
||||||
|
|
||||||
def test_split_keep_original(self):
|
def test_split_keep_original(self, split_test_case):
|
||||||
"""Test if splitting with keep=True yields the original string."""
|
"""Test if splitting with keep=True yields the original string."""
|
||||||
for case in test_data.strip().splitlines():
|
items = split.split(split_test_case.input, keep=True)
|
||||||
cmd = case.split('/')[0]
|
assert ''.join(items) == split_test_case.input
|
||||||
with self.subTest(cmd=cmd):
|
|
||||||
items = split.split(cmd, keep=True)
|
|
||||||
self.assertEqual(''.join(items), cmd)
|
|
||||||
|
|
||||||
def test_split_keep(self):
|
def test_split_keep(self, split_test_case):
|
||||||
"""Test splitting with keep=True."""
|
"""Test splitting with keep=True."""
|
||||||
for case in test_data.strip().splitlines():
|
items = split.split(split_test_case.input, keep=True)
|
||||||
cmd, _mid, out = case.split('/')[:-1]
|
assert items == split_test_case.no_keep
|
||||||
with self.subTest(cmd=cmd):
|
|
||||||
items = split.split(cmd, keep=True)
|
|
||||||
self.assertEqual(items, out.split('|'))
|
|
||||||
|
|
||||||
|
|
||||||
class SimpleSplitTests(unittest.TestCase):
|
class TestSimpleSplit:
|
||||||
|
|
||||||
"""Test simple_split."""
|
"""Test simple_split."""
|
||||||
|
|
||||||
@ -145,27 +166,20 @@ class SimpleSplitTests(unittest.TestCase):
|
|||||||
'foo\nbar': ['foo', '\nbar'],
|
'foo\nbar': ['foo', '\nbar'],
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_str_split(self):
|
@pytest.mark.parametrize('test', TESTS)
|
||||||
|
def test_str_split(self, test):
|
||||||
"""Test if the behavior matches str.split."""
|
"""Test if the behavior matches str.split."""
|
||||||
for test in self.TESTS:
|
assert split.simple_split(test) == test.rstrip().split()
|
||||||
with self.subTest(string=test):
|
|
||||||
self.assertEqual(split.simple_split(test),
|
|
||||||
test.rstrip().split())
|
|
||||||
|
|
||||||
def test_str_split_maxsplit_1(self):
|
@pytest.mark.parametrize('s, maxsplit',
|
||||||
"""Test if the behavior matches str.split with maxsplit=1."""
|
[("foo bar baz", 1), (" foo bar baz ", 0)])
|
||||||
string = "foo bar baz"
|
def test_str_split_maxsplit(self, s, maxsplit):
|
||||||
self.assertEqual(split.simple_split(string, maxsplit=1),
|
"""Test if the behavior matches str.split with given maxsplit."""
|
||||||
string.rstrip().split(maxsplit=1))
|
actual = split.simple_split(s, maxsplit=maxsplit)
|
||||||
|
expected = s.rstrip().split(maxsplit=maxsplit)
|
||||||
|
assert actual == expected
|
||||||
|
|
||||||
def test_str_split_maxsplit_0(self):
|
@pytest.mark.parametrize('test, expected', TESTS.items())
|
||||||
"""Test if the behavior matches str.split with maxsplit=0."""
|
def test_split_keep(self, test, expected):
|
||||||
string = " foo bar baz "
|
|
||||||
self.assertEqual(split.simple_split(string, maxsplit=0),
|
|
||||||
string.rstrip().split(maxsplit=0))
|
|
||||||
|
|
||||||
def test_split_keep(self):
|
|
||||||
"""Test splitting with keep=True."""
|
"""Test splitting with keep=True."""
|
||||||
for test, expected in self.TESTS.items():
|
assert split.simple_split(test, keep=True) == expected
|
||||||
with self.subTest(string=test):
|
|
||||||
self.assertEqual(split.simple_split(test, keep=True), expected)
|
|
||||||
|
70
tests/utils/overflow_test_cases.py
Normal file
70
tests/utils/overflow_test_cases.py
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
|
||||||
|
|
||||||
|
# Copyright 2014-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/>.
|
||||||
|
|
||||||
|
"""
|
||||||
|
Provides test data for overflow checking.
|
||||||
|
|
||||||
|
Module attributes:
|
||||||
|
INT32_MIN: Minimum valid value for a signed int32.
|
||||||
|
INT32_MAX: Maximum valid value for a signed int32.
|
||||||
|
INT64_MIN: Minimum valid value for a signed int64.
|
||||||
|
INT64_MAX: Maximum valid value for a signed int64.
|
||||||
|
GOOD_VALUES: A dict of types mapped to a list of good values.
|
||||||
|
BAD_VALUES: A dict of types mapped to a list of bad values.
|
||||||
|
"""
|
||||||
|
|
||||||
|
INT32_MIN = -(2 ** 31)
|
||||||
|
INT32_MAX = 2 ** 31 - 1
|
||||||
|
INT64_MIN = -(2 ** 63)
|
||||||
|
INT64_MAX = 2 ** 63 - 1
|
||||||
|
|
||||||
|
GOOD_VALUES = {
|
||||||
|
'int': [-1, 0, 1, 23.42, INT32_MIN, INT32_MAX],
|
||||||
|
'int64': [-1, 0, 1, 23.42, INT64_MIN, INT64_MAX],
|
||||||
|
}
|
||||||
|
|
||||||
|
BAD_VALUES = {
|
||||||
|
'int': [(INT32_MIN - 1, INT32_MIN),
|
||||||
|
(INT32_MAX + 1, INT32_MAX),
|
||||||
|
(float(INT32_MAX + 1), INT32_MAX)],
|
||||||
|
'int64': [(INT64_MIN - 1, INT64_MIN),
|
||||||
|
(INT64_MAX + 1, INT64_MAX),
|
||||||
|
(float(INT64_MAX + 1), INT64_MAX)],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def iter_good_values():
|
||||||
|
"""Yield "good" (C data type, value) tuples.
|
||||||
|
|
||||||
|
Those should pass overflow checking.
|
||||||
|
"""
|
||||||
|
for ctype, values in GOOD_VALUES.items():
|
||||||
|
for value in values:
|
||||||
|
yield ctype, value
|
||||||
|
|
||||||
|
|
||||||
|
def iter_bad_values():
|
||||||
|
"""Yield pairs of "bad" (C type, value, repl) tuples.
|
||||||
|
|
||||||
|
Theose should not pass overflow checking. The third value is the value they
|
||||||
|
should be replaced with if overflow checking should not be fatal.
|
||||||
|
"""
|
||||||
|
for ctype, values in BAD_VALUES.items():
|
||||||
|
for value, repl in values:
|
||||||
|
yield ctype, value, repl
|
@ -20,34 +20,34 @@
|
|||||||
"""Tests for qutebrowser.utils.jinja."""
|
"""Tests for qutebrowser.utils.jinja."""
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
import unittest
|
|
||||||
import unittest.mock
|
import pytest
|
||||||
|
|
||||||
from qutebrowser.utils import jinja
|
from qutebrowser.utils import jinja
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def patch_read_file(monkeypatch):
|
||||||
|
"""pytest fixture to patch utils.read_file."""
|
||||||
def _read_file(path):
|
def _read_file(path):
|
||||||
"""Mocked utils.read_file."""
|
"""A read_file which returns a simple template if the path is right."""
|
||||||
if path == os.path.join('html', 'test.html'):
|
if path == os.path.join('html', 'test.html'):
|
||||||
return """Hello {{var}}"""
|
return """Hello {{var}}"""
|
||||||
else:
|
else:
|
||||||
raise ValueError("Invalid path {}!".format(path))
|
raise ValueError("Invalid path {}!".format(path))
|
||||||
|
|
||||||
|
monkeypatch.setattr('qutebrowser.utils.jinja.utils.read_file', _read_file)
|
||||||
|
|
||||||
@unittest.mock.patch('qutebrowser.utils.jinja.utils.read_file')
|
|
||||||
class JinjaTests(unittest.TestCase):
|
|
||||||
|
|
||||||
"""Tests for getting template via jinja."""
|
def test_simple_template():
|
||||||
|
|
||||||
def test_simple_template(self, readfile_mock):
|
|
||||||
"""Test with a simple template."""
|
"""Test with a simple template."""
|
||||||
readfile_mock.side_effect = _read_file
|
|
||||||
template = jinja.env.get_template('test.html')
|
template = jinja.env.get_template('test.html')
|
||||||
# https://bitbucket.org/logilab/pylint/issue/490/
|
# https://bitbucket.org/logilab/pylint/issue/490/
|
||||||
data = template.render(var='World') # pylint: disable=no-member
|
data = template.render(var='World') # pylint: disable=no-member
|
||||||
self.assertEqual(data, "Hello World")
|
assert data == "Hello World"
|
||||||
|
|
||||||
def test_utf8(self, readfile_mock):
|
|
||||||
|
def test_utf8():
|
||||||
"""Test rendering with an UTF8 template.
|
"""Test rendering with an UTF8 template.
|
||||||
|
|
||||||
This was an attempt to get a failing test case for #127 but it seems
|
This was an attempt to get a failing test case for #127 but it seems
|
||||||
@ -55,12 +55,7 @@ class JinjaTests(unittest.TestCase):
|
|||||||
|
|
||||||
https://github.com/The-Compiler/qutebrowser/issues/127
|
https://github.com/The-Compiler/qutebrowser/issues/127
|
||||||
"""
|
"""
|
||||||
readfile_mock.side_effect = _read_file
|
|
||||||
template = jinja.env.get_template('test.html')
|
template = jinja.env.get_template('test.html')
|
||||||
# https://bitbucket.org/logilab/pylint/issue/490/
|
# https://bitbucket.org/logilab/pylint/issue/490/
|
||||||
data = template.render(var='\u2603') # pylint: disable=no-member
|
data = template.render(var='\u2603') # pylint: disable=no-member
|
||||||
self.assertEqual(data, "Hello \u2603")
|
assert data == "Hello \u2603"
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
unittest.main()
|
|
||||||
|
@ -20,107 +20,74 @@
|
|||||||
"""Tests for qutebrowser.utils.qtutils."""
|
"""Tests for qutebrowser.utils.qtutils."""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from qutebrowser import qutebrowser
|
from qutebrowser import qutebrowser
|
||||||
from qutebrowser.utils import qtutils
|
from qutebrowser.utils import qtutils
|
||||||
|
import overflow_test_cases
|
||||||
|
|
||||||
|
|
||||||
class CheckOverflowTests(unittest.TestCase):
|
class TestCheckOverflow:
|
||||||
|
|
||||||
"""Test check_overflow.
|
"""Test check_overflow."""
|
||||||
|
|
||||||
Class attributes:
|
@pytest.mark.parametrize('ctype, val',
|
||||||
INT32_MIN: Minimum valid value for a signed int32.
|
overflow_test_cases.iter_good_values())
|
||||||
INT32_MAX: Maximum valid value for a signed int32.
|
def test_good_values(self, ctype, val):
|
||||||
INT64_MIN: Minimum valid value for a signed int64.
|
|
||||||
INT64_MAX: Maximum valid value for a signed int64.
|
|
||||||
GOOD_VALUES: A dict of types mapped to a list of good values.
|
|
||||||
BAD_VALUES: A dict of types mapped to a list of bad values.
|
|
||||||
"""
|
|
||||||
|
|
||||||
INT32_MIN = -(2 ** 31)
|
|
||||||
INT32_MAX = 2 ** 31 - 1
|
|
||||||
INT64_MIN = -(2 ** 63)
|
|
||||||
INT64_MAX = 2 ** 63 - 1
|
|
||||||
|
|
||||||
GOOD_VALUES = {
|
|
||||||
'int': [-1, 0, 1, 23.42, INT32_MIN, INT32_MAX],
|
|
||||||
'int64': [-1, 0, 1, 23.42, INT64_MIN, INT64_MAX],
|
|
||||||
}
|
|
||||||
|
|
||||||
BAD_VALUES = {
|
|
||||||
'int': [(INT32_MIN - 1, INT32_MIN),
|
|
||||||
(INT32_MAX + 1, INT32_MAX),
|
|
||||||
(float(INT32_MAX + 1), INT32_MAX)],
|
|
||||||
'int64': [(INT64_MIN - 1, INT64_MIN),
|
|
||||||
(INT64_MAX + 1, INT64_MAX),
|
|
||||||
(float(INT64_MAX + 1), INT64_MAX)],
|
|
||||||
}
|
|
||||||
|
|
||||||
def test_good_values(self):
|
|
||||||
"""Test values which are inside bounds."""
|
"""Test values which are inside bounds."""
|
||||||
for ctype, vals in self.GOOD_VALUES.items():
|
|
||||||
for val in vals:
|
|
||||||
with self.subTest(ctype=ctype, val=val):
|
|
||||||
qtutils.check_overflow(val, ctype)
|
qtutils.check_overflow(val, ctype)
|
||||||
|
|
||||||
def test_bad_values_fatal(self):
|
@pytest.mark.parametrize('ctype, val',
|
||||||
|
[(ctype, val) for (ctype, val, _) in
|
||||||
|
overflow_test_cases.iter_bad_values()])
|
||||||
|
def test_bad_values_fatal(self, ctype, val):
|
||||||
"""Test values which are outside bounds with fatal=True."""
|
"""Test values which are outside bounds with fatal=True."""
|
||||||
for ctype, vals in self.BAD_VALUES.items():
|
with pytest.raises(OverflowError):
|
||||||
for (val, _) in vals:
|
|
||||||
with self.subTest(ctype=ctype, val=val):
|
|
||||||
with self.assertRaises(OverflowError):
|
|
||||||
qtutils.check_overflow(val, ctype)
|
qtutils.check_overflow(val, ctype)
|
||||||
|
|
||||||
def test_bad_values_nonfatal(self):
|
@pytest.mark.parametrize('ctype, val, repl',
|
||||||
|
overflow_test_cases.iter_bad_values())
|
||||||
|
def test_bad_values_nonfatal(self, ctype, val, repl):
|
||||||
"""Test values which are outside bounds with fatal=False."""
|
"""Test values which are outside bounds with fatal=False."""
|
||||||
for ctype, vals in self.BAD_VALUES.items():
|
|
||||||
for (val, replacement) in vals:
|
|
||||||
with self.subTest(ctype=ctype, val=val):
|
|
||||||
newval = qtutils.check_overflow(val, ctype, fatal=False)
|
newval = qtutils.check_overflow(val, ctype, fatal=False)
|
||||||
self.assertEqual(newval, replacement)
|
assert newval == repl
|
||||||
|
|
||||||
|
|
||||||
def argparser_exit(status=0, message=None): # pylint: disable=unused-argument
|
class TestGetQtArgs:
|
||||||
"""Function to monkey-patch .exit() of the argparser so it doesn't exit."""
|
|
||||||
raise Exception
|
|
||||||
|
|
||||||
|
|
||||||
class GetQtArgsTests(unittest.TestCase):
|
|
||||||
|
|
||||||
"""Tests for get_args."""
|
"""Tests for get_args."""
|
||||||
|
|
||||||
def setUp(self):
|
@pytest.fixture
|
||||||
self.parser = qutebrowser.get_argparser()
|
def parser(self, mocker):
|
||||||
self.parser.exit = argparser_exit
|
"""Fixture to provide an argparser.
|
||||||
|
|
||||||
def test_no_qt_args(self):
|
Monkey-patches .exit() of the argparser so it doesn't exit on errors.
|
||||||
|
"""
|
||||||
|
parser = qutebrowser.get_argparser()
|
||||||
|
mocker.patch.object(parser, 'exit', side_effect=Exception)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
def test_no_qt_args(self, parser):
|
||||||
"""Test commandline with no Qt arguments given."""
|
"""Test commandline with no Qt arguments given."""
|
||||||
args = self.parser.parse_args(['--debug'])
|
args = parser.parse_args(['--debug'])
|
||||||
self.assertEqual(qtutils.get_args(args), [sys.argv[0]])
|
assert qtutils.get_args(args) == [sys.argv[0]]
|
||||||
|
|
||||||
def test_qt_flag(self):
|
def test_qt_flag(self, parser):
|
||||||
"""Test commandline with a Qt flag."""
|
"""Test commandline with a Qt flag."""
|
||||||
args = self.parser.parse_args(['--debug', '--qt-reverse', '--nocolor'])
|
args = parser.parse_args(['--debug', '--qt-reverse', '--nocolor'])
|
||||||
self.assertEqual(qtutils.get_args(args), [sys.argv[0], '-reverse'])
|
assert qtutils.get_args(args) == [sys.argv[0], '-reverse']
|
||||||
|
|
||||||
def test_qt_arg(self):
|
def test_qt_arg(self, parser):
|
||||||
"""Test commandline with a Qt argument."""
|
"""Test commandline with a Qt argument."""
|
||||||
args = self.parser.parse_args(['--qt-stylesheet', 'foobar'])
|
args = parser.parse_args(['--qt-stylesheet', 'foobar'])
|
||||||
self.assertEqual(qtutils.get_args(args), [sys.argv[0], '-stylesheet',
|
assert qtutils.get_args(args) == [sys.argv[0], '-stylesheet', 'foobar']
|
||||||
'foobar'])
|
|
||||||
|
|
||||||
def test_qt_both(self):
|
def test_qt_both(self, parser):
|
||||||
"""Test commandline with a Qt argument and flag."""
|
"""Test commandline with a Qt argument and flag."""
|
||||||
args = self.parser.parse_args(['--qt-stylesheet', 'foobar',
|
args = parser.parse_args(['--qt-stylesheet', 'foobar', '--qt-reverse'])
|
||||||
'--qt-reverse'])
|
|
||||||
qt_args = qtutils.get_args(args)
|
qt_args = qtutils.get_args(args)
|
||||||
self.assertEqual(qt_args[0], sys.argv[0])
|
assert qt_args[0] == sys.argv[0]
|
||||||
self.assertIn('-reverse', qt_args)
|
assert '-reverse' in qt_args
|
||||||
self.assertIn('-stylesheet', qt_args)
|
assert '-stylesheet' in qt_args
|
||||||
self.assertIn('foobar', qt_args)
|
assert 'foobar' in qt_args
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
unittest.main()
|
|
||||||
|
6
tox.ini
6
tox.ini
@ -26,7 +26,7 @@ deps =
|
|||||||
# on Ubuntu Trusty.
|
# on Ubuntu Trusty.
|
||||||
commands =
|
commands =
|
||||||
{envpython} scripts/link_pyqt.py --tox {envdir}
|
{envpython} scripts/link_pyqt.py --tox {envdir}
|
||||||
{envpython} -m py.test {posargs}
|
{envpython} -m py.test --strict {posargs}
|
||||||
|
|
||||||
[testenv:coverage]
|
[testenv:coverage]
|
||||||
deps =
|
deps =
|
||||||
@ -36,7 +36,7 @@ deps =
|
|||||||
cov-core==1.15.0
|
cov-core==1.15.0
|
||||||
commands =
|
commands =
|
||||||
{[testenv:mkvenv]commands}
|
{[testenv:mkvenv]commands}
|
||||||
{envpython} -m py.test --cov qutebrowser --cov-report term --cov-report html {posargs}
|
{envpython} -m py.test --strict --cov qutebrowser --cov-report term --cov-report html {posargs}
|
||||||
|
|
||||||
[testenv:misc]
|
[testenv:misc]
|
||||||
commands =
|
commands =
|
||||||
@ -109,3 +109,5 @@ commands =
|
|||||||
|
|
||||||
[pytest]
|
[pytest]
|
||||||
norecursedirs = .tox .venv
|
norecursedirs = .tox .venv
|
||||||
|
markers =
|
||||||
|
gui: Tests using the GUI (e.g. spawning widgets)
|
||||||
|
Loading…
Reference in New Issue
Block a user