Merge pull request #16 from hackebrot/layout

Adopt test layout to pytest.
This commit is contained in:
Florian Bruhin 2015-04-08 14:03:02 +02:00
commit 76651822bd
53 changed files with 2305 additions and 2281 deletions

View File

@ -40,6 +40,7 @@ argument-rgx=[a-z_][a-z0-9_]{0,30}$
variable-rgx=[a-z_][a-z0-9_]{0,30}$
class-attribute-rgx=[A-Za-z_][A-Za-z0-9_]{1,30}$
inlinevar-rgx=[a-z_][a-z0-9_]*$
docstring-min-length=2
[FORMAT]
max-line-length=79

View File

@ -6,7 +6,7 @@ graft icons
graft scripts/pylint_checkers
graft doc/img
graft misc
include qutebrowser/test/testfile
include qutebrowser/utils/testfile
include qutebrowser/git-commit-id
include COPYING doc/* README.asciidoc
include qutebrowser.desktop
@ -24,7 +24,7 @@ exclude scripts/segfault_test.sh
exclude doc/notes
exclude CONTRIBUTING.asciidoc
recursive-exclude doc *.asciidoc
prune test
prune tests
exclude qutebrowser.rcc
exclude .coveragerc
exclude .flake8

View File

@ -1,20 +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/>.
"""Tests for the qutebrowser.browser package."""

View File

@ -1,20 +0,0 @@
# 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/>.
"""Tests for the qutebrowser.browser.http module."""

View File

@ -1,140 +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/>.
"""Tests for webelement.tabhistory."""
import unittest
from PyQt5.QtCore import QUrl, QPoint
from qutebrowser.browser import tabhistory
from qutebrowser.browser.tabhistory import TabHistoryItem as Item
from qutebrowser.utils import qtutils
from qutebrowser.test import helpers
class SerializeHistoryTests(unittest.TestCase):
"""Tests for serialize()."""
def setUp(self):
self.page = helpers.get_webpage()
self.history = self.page.history()
self.assertEqual(self.history.count(), 0)
self.items = [
Item(QUrl('https://www.heise.de/'), QUrl('http://www.heise.de/'),
'heise'),
Item(QUrl('http://example.com/%E2%80%A6'),
QUrl('http://example.com/%E2%80%A6'), 'percent', active=True),
Item(QUrl('http://example.com/?foo=bar'),
QUrl('http://original.url.example.com/'), 'arg',
user_data={'foo': 23, 'bar': 42}),
# From https://github.com/OtterBrowser/otter-browser/issues/709#issuecomment-74749471
Item(QUrl('http://github.com/OtterBrowser/24/134/2344/otter-browser/issues/709/'),
QUrl('http://github.com/OtterBrowser/24/134/2344/otter-browser/issues/709/'),
'Page not found | github',
user_data={'zoom': 149, 'scroll-pos': QPoint(0, 0)}),
Item(QUrl('https://mail.google.com/mail/u/0/#label/some+label/234lkjsd0932lkjf884jqwerdf4'),
QUrl('https://mail.google.com/mail/u/0/#label/some+label/234lkjsd0932lkjf884jqwerdf4'),
'"some label" - email@gmail.com - Gmail"',
user_data={'zoom': 120, 'scroll-pos': QPoint(0, 0)}),
]
stream, _data, self.user_data = tabhistory.serialize(self.items)
qtutils.deserialize_stream(stream, self.history)
def test_count(self):
"""Check if the history's count was loaded correctly."""
with helpers.disable_logger('qt'):
self.assertEqual(self.history.count(), len(self.items))
def test_valid(self):
"""Check if all items are valid."""
for i, _item in enumerate(self.items):
self.assertTrue(self.history.itemAt(i).isValid())
def test_no_userdata(self):
"""Check if all items have no user data."""
for i, _item in enumerate(self.items):
self.assertIsNone(self.history.itemAt(i).userData())
def test_userdata(self):
"""Check if all user data has been restored to self.user_data."""
for item, user_data in zip(self.items, self.user_data):
self.assertEqual(user_data, item.user_data)
def test_currentitem(self):
"""Check if the current item index was loaded correctly."""
self.assertEqual(self.history.currentItemIndex(), 1)
def test_urls(self):
"""Check if the URLs were loaded correctly."""
for i, item in enumerate(self.items):
with self.subTest(i=i, item=item):
self.assertEqual(self.history.itemAt(i).url(), item.url)
def test_original_urls(self):
"""Check if the original URLs were loaded correctly."""
for i, item in enumerate(self.items):
with self.subTest(i=i, item=item):
self.assertEqual(self.history.itemAt(i).originalUrl(),
item.original_url)
def test_titles(self):
"""Check if the titles were loaded correctly."""
for i, item in enumerate(self.items):
with self.subTest(i=i, item=item):
self.assertEqual(self.history.itemAt(i).title(), item.title)
class SerializeHistorySpecialTests(unittest.TestCase):
"""Tests for serialize() without items set up in setUp."""
def setUp(self):
self.page = helpers.get_webpage()
self.history = self.page.history()
self.assertEqual(self.history.count(), 0)
def test_no_active_item(self):
"""Check tabhistory.serialize with no active item."""
items = [Item(QUrl(), QUrl(), '')]
with self.assertRaises(ValueError):
tabhistory.serialize(items)
def test_two_active_items(self):
"""Check tabhistory.serialize with two active items."""
items = [Item(QUrl(), QUrl(), '', active=True),
Item(QUrl(), QUrl(), ''),
Item(QUrl(), QUrl(), '', active=True)]
with self.assertRaises(ValueError):
tabhistory.serialize(items)
def test_empty(self):
"""Check tabhistory.serialize with no items."""
items = []
stream, _data, user_data = tabhistory.serialize(items)
qtutils.deserialize_stream(stream, self.history)
self.assertEqual(self.history.count(), 0)
self.assertEqual(self.history.currentItemIndex(), 0)
self.assertFalse(user_data)
if __name__ == '__main__':
unittest.main()

View File

@ -1 +0,0 @@
"""Tests for qutebrowser.config."""

View File

@ -1,109 +0,0 @@
# 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/>.
"""Helpers needed by tests."""
import os
import logging
import contextlib
from unittest import mock
from PyQt5.QtGui import QKeyEvent
from PyQt5.QtWebKitWidgets import QWebPage
from PyQt5.QtNetwork import QNetworkAccessManager
unicode_encode_err = UnicodeEncodeError('ascii', # codec
'', # object
0, # start
2, # end
'fake exception') # reason
@contextlib.contextmanager
def environ_set_temp(env):
"""Set temporary environment variables.
Args:
env: A dictionary with name: value pairs.
If value is None, the variable is temporarily deleted.
"""
old_env = {}
for name, value in env.items():
try:
old_env[name] = os.environ[name]
except KeyError:
pass
if value is None:
os.environ.pop(name, None)
else:
os.environ[name] = value
yield
for name, value in env.items():
if name in old_env:
os.environ[name] = old_env[name]
elif value is not None:
del os.environ[name]
@contextlib.contextmanager
def disable_logger(name):
"""Temporarily disable a logger."""
logging.getLogger(name).propagate = False
try:
yield
finally:
logging.getLogger(name).propagate = True
def fake_keyevent(key, modifiers=0, text=''):
"""Generate a new fake QKeyPressEvent."""
evtmock = mock.create_autospec(QKeyEvent, instance=True)
evtmock.key.return_value = key
evtmock.modifiers.return_value = modifiers
evtmock.text.return_value = text
return evtmock
def get_webpage():
"""Get a new QWebPage object."""
page = QWebPage()
nam = page.networkAccessManager()
nam.setNetworkAccessible(QNetworkAccessManager.NotAccessible)
return page
class MessageModule:
"""A drop-in replacement for qutebrowser.utils.message."""
def error(self, _win_id, message, _immediately=False):
"""Log an error to the message logger."""
logging.getLogger('message').error(message)
def warning(self, _win_id, message, _immediately=False):
"""Log a warning to the message logger."""
logging.getLogger('message').warning(message)
def info(self, _win_id, message, _immediately=True):
"""Log an info message to the message logger."""
logging.getLogger('message').info(message)

View File

@ -1 +0,0 @@
"""Tests for qutebrowser.keyinput."""

View File

@ -1,299 +0,0 @@
# 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/>.
# pylint: disable=protected-access
"""Tests for BaseKeyParser."""
import logging
import unittest
from unittest import mock
from PyQt5.QtCore import Qt
from qutebrowser.keyinput import basekeyparser
from qutebrowser.test import stubs, helpers
from qutebrowser.utils import objreg, log
CONFIG = {'input': {'timeout': 100}}
BINDINGS = {'test': {'<Ctrl-a>': 'ctrla',
'a': 'a',
'ba': 'ba',
'ax': 'ax',
'ccc': 'ccc'},
'test2': {'foo': 'bar', '<Ctrl+X>': 'ctrlx'}}
fake_keyconfig = mock.Mock(spec=['get_bindings_for'])
fake_keyconfig.get_bindings_for.side_effect = lambda s: BINDINGS[s]
class SplitCountTests(unittest.TestCase):
"""Test the _split_count method.
Attributes:
kp: The BaseKeyParser we're testing.
"""
def setUp(self):
self.kp = basekeyparser.BaseKeyParser(0, supports_count=True)
def test_onlycount(self):
"""Test split_count with only a count."""
self.kp._keystring = '10'
self.assertEqual(self.kp._split_count(), (10, ''))
def test_normalcount(self):
"""Test split_count with count and text."""
self.kp._keystring = '10foo'
self.assertEqual(self.kp._split_count(), (10, 'foo'))
def test_minuscount(self):
"""Test split_count with a negative count."""
self.kp._keystring = '-1foo'
self.assertEqual(self.kp._split_count(), (None, '-1foo'))
def test_expcount(self):
"""Test split_count with an exponential count."""
self.kp._keystring = '10e4foo'
self.assertEqual(self.kp._split_count(), (10, 'e4foo'))
def test_nocount(self):
"""Test split_count with only a command."""
self.kp._keystring = 'foo'
self.assertEqual(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'
self.assertEqual(self.kp._split_count(), (None, '10foo'))
@mock.patch('qutebrowser.keyinput.basekeyparser.usertypes.Timer',
new=stubs.FakeTimer)
class ReadConfigTests(unittest.TestCase):
"""Test reading the config."""
def setUp(self):
objreg.register('key-config', fake_keyconfig)
def tearDown(self):
objreg.delete('key-config')
def test_read_config_invalid(self):
"""Test reading config without setting it before."""
kp = basekeyparser.BaseKeyParser(0)
with self.assertRaises(ValueError):
kp.read_config()
def test_read_config_valid(self):
"""Test reading config."""
kp = basekeyparser.BaseKeyParser(0, supports_count=True,
supports_chains=True)
kp.read_config('test')
self.assertIn('ccc', kp.bindings)
self.assertIn('ctrl+a', kp.special_bindings)
kp.read_config('test2')
self.assertNotIn('ccc', kp.bindings)
self.assertNotIn('ctrl+a', kp.special_bindings)
self.assertIn('foo', kp.bindings)
self.assertIn('ctrl+x', kp.special_bindings)
@mock.patch('qutebrowser.keyinput.basekeyparser.usertypes.Timer',
new=stubs.FakeTimer)
class SpecialKeysTests(unittest.TestCase):
"""Check execute() with special keys.
Attributes:
kp: The BaseKeyParser to be tested.
"""
def setUp(self):
objreg.register('key-config', fake_keyconfig)
self.kp = basekeyparser.BaseKeyParser(0)
self.kp.execute = mock.Mock()
with self.assertLogs(log.keyboard, logging.WARNING):
# Ignoring keychain 'ccc' in mode 'test' because keychains are not
# supported there.
self.kp.read_config('test')
def tearDown(self):
objreg.delete('key-config')
def test_valid_key(self):
"""Test a valid special keyevent."""
self.kp.handle(helpers.fake_keyevent(Qt.Key_A, Qt.ControlModifier))
self.kp.handle(helpers.fake_keyevent(Qt.Key_X, Qt.ControlModifier))
self.kp.execute.assert_called_once_with('ctrla', self.kp.Type.special)
def test_invalid_key(self):
"""Test an invalid special keyevent."""
self.kp.handle(helpers.fake_keyevent(Qt.Key_A, (Qt.ControlModifier |
Qt.AltModifier)))
self.assertFalse(self.kp.execute.called)
def test_keychain(self):
"""Test a keychain."""
self.kp.handle(helpers.fake_keyevent(Qt.Key_B))
self.kp.handle(helpers.fake_keyevent(Qt.Key_A))
self.assertFalse(self.kp.execute.called)
@mock.patch('qutebrowser.keyinput.basekeyparser.usertypes.Timer',
new=stubs.FakeTimer)
class KeyChainTests(unittest.TestCase):
"""Test execute() with keychain support.
Attributes:
kp: The BaseKeyParser to be tested.
"""
def setUp(self):
"""Set up mocks and read the test config."""
objreg.register('key-config', fake_keyconfig)
self.kp = basekeyparser.BaseKeyParser(0, supports_chains=True,
supports_count=False)
self.kp.execute = mock.Mock()
self.kp.read_config('test')
def tearDown(self):
objreg.delete('key-config')
def test_valid_special_key(self):
"""Test valid special key."""
self.kp.handle(helpers.fake_keyevent(Qt.Key_A, Qt.ControlModifier))
self.kp.handle(helpers.fake_keyevent(Qt.Key_X, Qt.ControlModifier))
self.kp.execute.assert_called_once_with('ctrla', self.kp.Type.special)
self.assertEqual(self.kp._keystring, '')
def test_invalid_special_key(self):
"""Test invalid special key."""
self.kp.handle(helpers.fake_keyevent(Qt.Key_A, (Qt.ControlModifier |
Qt.AltModifier)))
self.assertFalse(self.kp.execute.called)
self.assertEqual(self.kp._keystring, '')
def test_keychain(self):
"""Test valid keychain."""
# Press 'x' which is ignored because of no match
self.kp.handle(helpers.fake_keyevent(Qt.Key_X, text='x'))
# Then start the real chain
self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='b'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='a'))
self.kp.execute.assert_called_once_with('ba', self.kp.Type.chain, None)
self.assertEqual(self.kp._keystring, '')
@mock.patch('qutebrowser.keyinput.basekeyparser.config',
new=stubs.ConfigStub(CONFIG))
def test_ambiguous_keychain(self):
"""Test ambiguous keychain."""
timer = self.kp._ambiguous_timer
self.assertFalse(timer.isActive())
# We start with 'a' where the keychain gives us an ambiguous result.
# Then we check if the timer has been set up correctly
self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='a'))
self.assertFalse(self.kp.execute.called)
self.assertTrue(timer.isSingleShot())
self.assertEqual(timer.interval(), 100)
self.assertTrue(timer.isActive())
# Now we type an 'x' and check 'ax' has been executed and the timer
# stopped.
self.kp.handle(helpers.fake_keyevent(Qt.Key_X, text='x'))
self.kp.execute.assert_called_once_with('ax', self.kp.Type.chain, None)
self.assertFalse(timer.isActive())
self.assertEqual(self.kp._keystring, '')
def test_invalid_keychain(self):
"""Test invalid keychain."""
self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='b'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_C, text='c'))
self.assertEqual(self.kp._keystring, '')
@mock.patch('qutebrowser.keyinput.basekeyparser.usertypes.Timer',
new=stubs.FakeTimer)
class CountTests(unittest.TestCase):
"""Test execute() with counts."""
def setUp(self):
objreg.register('key-config', fake_keyconfig)
self.kp = basekeyparser.BaseKeyParser(0, supports_chains=True,
supports_count=True)
self.kp.execute = mock.Mock()
self.kp.read_config('test')
def test_no_count(self):
"""Test with no count added."""
self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='b'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='a'))
self.kp.execute.assert_called_once_with('ba', self.kp.Type.chain, None)
self.assertEqual(self.kp._keystring, '')
def test_count_0(self):
"""Test with count=0."""
self.kp.handle(helpers.fake_keyevent(Qt.Key_0, text='0'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='b'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='a'))
self.kp.execute.assert_called_once_with('ba', self.kp.Type.chain, 0)
self.assertEqual(self.kp._keystring, '')
def test_count_42(self):
"""Test with count=42."""
self.kp.handle(helpers.fake_keyevent(Qt.Key_4, text='4'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_2, text='2'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='b'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='a'))
self.kp.execute.assert_called_once_with('ba', self.kp.Type.chain, 42)
self.assertEqual(self.kp._keystring, '')
def test_count_42_invalid(self):
"""Test with count=42 and invalid command."""
# Invalid call with ccx gets ignored
self.kp.handle(helpers.fake_keyevent(Qt.Key_4, text='4'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_2, text='2'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='c'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='c'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='x'))
self.assertFalse(self.kp.execute.called)
self.assertEqual(self.kp._keystring, '')
# Valid call with ccc gets the correct count
self.kp.handle(helpers.fake_keyevent(Qt.Key_4, text='2'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_2, text='3'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='c'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='c'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='c'))
self.kp.execute.assert_called_once_with('ccc', self.kp.Type.chain, 23)
self.assertEqual(self.kp._keystring, '')
def tearDown(self):
objreg.global_registry.clear()
if __name__ == '__main__':
unittest.main()

View File

@ -1,20 +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/>.
"""The qutebrowser test suite."""

View File

@ -1,84 +0,0 @@
# 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 test helpers."""
import os
import unittest
from qutebrowser.test import helpers
class TestEnvironSetTemp(unittest.TestCase):
"""Test the environ_set_temp helper."""
def test_environ_set(self):
"""Test environ_set_temp with something which was set already."""
os.environ['QUTEBROWSER_ENVIRON_TEST'] = 'oldval'
with helpers.environ_set_temp({'QUTEBROWSER_ENVIRON_TEST': 'newval'}):
self.assertEqual(os.environ['QUTEBROWSER_ENVIRON_TEST'], 'newval')
self.assertEqual(os.environ['QUTEBROWSER_ENVIRON_TEST'], 'oldval')
def test_environ_unset(self):
"""Test environ_set_temp with something which wasn't set yet."""
with helpers.environ_set_temp({'QUTEBROWSER_ENVIRON_TEST': 'newval'}):
self.assertEqual(os.environ['QUTEBROWSER_ENVIRON_TEST'], 'newval')
self.assertNotIn('QUTEBROWSER_ENVIRON_TEST', os.environ)
def test_environ_multiple(self):
"""Test environ_set_temp with multiple values."""
os.environ['QUTEBROWSER_ENVIRON_TEST_1'] = 'oldval_1'
os.environ['QUTEBROWSER_ENVIRON_TEST_3'] = 'oldval_3'
env = {
'QUTEBROWSER_ENVIRON_TEST_1': 'newval_1',
'QUTEBROWSER_ENVIRON_TEST_2': 'newval_2',
'QUTEBROWSER_ENVIRON_TEST_3': None,
}
with helpers.environ_set_temp(env):
self.assertEqual(os.environ['QUTEBROWSER_ENVIRON_TEST_1'],
'newval_1')
self.assertEqual(os.environ['QUTEBROWSER_ENVIRON_TEST_2'],
'newval_2')
self.assertNotIn('QUTEBROWSER_ENVIRON_TEST_3', os.environ)
self.assertEqual(os.environ['QUTEBROWSER_ENVIRON_TEST_1'], 'oldval_1')
self.assertNotIn('QUTEBROWSER_ENVIRON_TEST_2', os.environ)
self.assertEqual(os.environ['QUTEBROWSER_ENVIRON_TEST_3'], 'oldval_3')
def test_environ_none_set(self):
"""Test environ_set_temp with something which was set already."""
os.environ['QUTEBROWSER_ENVIRON_TEST'] = 'oldval'
with helpers.environ_set_temp({'QUTEBROWSER_ENVIRON_TEST': None}):
self.assertNotIn('QUTEBROWSER_ENVIRON_TEST', os.environ)
self.assertEqual(os.environ['QUTEBROWSER_ENVIRON_TEST'], 'oldval')
def test_environ_none_unset(self):
"""Test environ_set_temp with something which wasn't set yet."""
with helpers.environ_set_temp({'QUTEBROWSER_ENVIRON_TEST': None}):
self.assertNotIn('QUTEBROWSER_ENVIRON_TEST', os.environ)
self.assertNotIn('QUTEBROWSER_ENVIRON_TEST', os.environ)
def tearDown(self):
if 'QUTEBROWSER_ENVIRON_TEST' in os.environ:
# if some test failed
del os.environ['QUTEBROWSER_ENVIRON_TEST']
if __name__ == '__main__':
unittest.main()

View File

@ -1,107 +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/>.
"""Test test stubs."""
import unittest
from unittest import mock
from qutebrowser.test import stubs
class TestFakeTimer(unittest.TestCase):
"""Test FakeTimer."""
def setUp(self):
self.timer = stubs.FakeTimer()
def test_timeout(self):
"""Test whether timeout calls the functions."""
func = mock.Mock()
func2 = mock.Mock()
self.timer.timeout.connect(func)
self.timer.timeout.connect(func2)
self.assertFalse(func.called)
self.assertFalse(func2.called)
self.timer.timeout.emit()
func.assert_called_once_with()
func2.assert_called_once_with()
def test_disconnect_all(self):
"""Test disconnect without arguments."""
func = mock.Mock()
self.timer.timeout.connect(func)
self.timer.timeout.disconnect()
self.timer.timeout.emit()
self.assertFalse(func.called)
def test_disconnect_one(self):
"""Test disconnect with a single argument."""
func = mock.Mock()
self.timer.timeout.connect(func)
self.timer.timeout.disconnect(func)
self.timer.timeout.emit()
self.assertFalse(func.called)
def test_disconnect_all_invalid(self):
"""Test disconnecting with no connections."""
with self.assertRaises(TypeError):
self.timer.timeout.disconnect()
def test_disconnect_one_invalid(self):
"""Test disconnecting with an invalid connection."""
func1 = mock.Mock()
func2 = mock.Mock()
self.timer.timeout.connect(func1)
with self.assertRaises(TypeError):
self.timer.timeout.disconnect(func2)
self.assertFalse(func1.called)
self.assertFalse(func2.called)
self.timer.timeout.emit()
func1.assert_called_once_with()
def test_singleshot(self):
"""Test setting singleShot."""
self.assertFalse(self.timer.singleShot())
self.timer.setSingleShot(True)
self.assertTrue(self.timer.singleShot())
self.timer.start()
self.assertTrue(self.timer.isActive())
self.timer.timeout.emit()
self.assertFalse(self.timer.isActive())
def test_active(self):
"""Test isActive."""
self.assertFalse(self.timer.isActive())
self.timer.start()
self.assertTrue(self.timer.isActive())
self.timer.stop()
self.assertFalse(self.timer.isActive())
def test_interval(self):
"""Test setting an interval."""
self.assertEqual(self.timer.interval(), 0)
self.timer.setInterval(1000)
self.assertEqual(self.timer.interval(), 1000)
if __name__ == '__main__':
unittest.main()

View File

@ -1,138 +0,0 @@
# 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/>.
"""Tests for qutebrowser.utils.standarddir."""
import os
import os.path
import sys
import shutil
import unittest
import tempfile
from qutebrowser.utils import standarddir
from qutebrowser.test import helpers, qApp
class GetStandardDirLinuxTests(unittest.TestCase):
"""Tests for standarddir under Linux.
Attributes:
temp_dir: A temporary directory.
old_name: The old applicationName.
"""
def setUp(self):
self.temp_dir = tempfile.mkdtemp()
self.old_name = qApp.applicationName()
qApp.setApplicationName('qutebrowser')
@unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
def test_data_explicit(self):
"""Test data dir with XDG_DATA_HOME explicitly set."""
with helpers.environ_set_temp({'XDG_DATA_HOME': self.temp_dir}):
standarddir.init(None)
expected = os.path.join(self.temp_dir, 'qutebrowser')
self.assertEqual(standarddir.data(), expected)
@unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
def test_config_explicit(self):
"""Test config dir with XDG_CONFIG_HOME explicitly set."""
with helpers.environ_set_temp({'XDG_CONFIG_HOME': self.temp_dir}):
standarddir.init(None)
expected = os.path.join(self.temp_dir, 'qutebrowser')
self.assertEqual(standarddir.config(), expected)
@unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
def test_cache_explicit(self):
"""Test cache dir with XDG_CACHE_HOME explicitly set."""
with helpers.environ_set_temp({'XDG_CACHE_HOME': self.temp_dir}):
standarddir.init(None)
expected = os.path.join(self.temp_dir, 'qutebrowser')
self.assertEqual(standarddir.cache(), expected)
@unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
def test_data(self):
"""Test data dir with XDG_DATA_HOME not set."""
env = {'HOME': self.temp_dir, 'XDG_DATA_HOME': None}
with helpers.environ_set_temp(env):
standarddir.init(None)
expected = os.path.join(self.temp_dir, '.local', 'share',
'qutebrowser')
self.assertEqual(standarddir.data(), expected)
@unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
def test_config(self):
"""Test config dir with XDG_CONFIG_HOME not set."""
env = {'HOME': self.temp_dir, 'XDG_CONFIG_HOME': None}
with helpers.environ_set_temp(env):
standarddir.init(None)
expected = os.path.join(self.temp_dir, '.config', 'qutebrowser')
self.assertEqual(standarddir.config(), expected)
@unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
def test_cache(self):
"""Test cache dir with XDG_CACHE_HOME not set."""
env = {'HOME': self.temp_dir, 'XDG_CACHE_HOME': None}
with helpers.environ_set_temp(env):
standarddir.init(None)
expected = os.path.join(self.temp_dir, '.cache', 'qutebrowser')
self.assertEqual(standarddir.cache(), expected)
def tearDown(self):
qApp.setApplicationName(self.old_name)
shutil.rmtree(self.temp_dir)
class GetStandardDirWindowsTests(unittest.TestCase):
"""Tests for standarddir under Windows.
Attributes:
old_name: The old applicationName.
"""
def setUp(self):
self.old_name = qApp.applicationName()
# We can't store the files in a temp dir, so we don't chose qutebrowser
qApp.setApplicationName('qutebrowser_test')
standarddir.init(None)
def tearDown(self):
qApp.setApplicationName(self.old_name)
@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
def test_data(self):
"""Test data dir."""
self.assertEqual(standarddir.data().split(os.sep)[-2:],
['qutebrowser_test', 'data'], standarddir.data())
@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
def test_config(self):
"""Test config dir."""
self.assertEqual(standarddir.config().split(os.sep)[-1],
'qutebrowser_test',
standarddir.config())
@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows")
def test_cache(self):
"""Test cache dir."""
self.assertEqual(standarddir.cache().split(os.sep)[-2:],
['qutebrowser_test', 'cache'], standarddir.cache())

View File

@ -1,20 +0,0 @@
# 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/>.
"""Tests for qutebrowser.utils.usertype."""

View File

@ -76,7 +76,7 @@ def check_spelling(target):
# Words which look better when splitted, but might need some fine tuning.
words |= {'keystrings', 'webelements', 'mouseevent', 'keysequence',
'normalmode', 'eventloops', 'sizehint', 'statemachine',
'metaobject', 'logrecord', 'monkeypatch', 'filetype'}
'metaobject', 'logrecord', 'filetype'}
seen = collections.defaultdict(list)
try:

View File

@ -0,0 +1,60 @@
#!/usr/bin/env python3
# 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/>.
"""Run pylint on tests.
This is needed because pylint can't check a folder which isn't a package:
https://bitbucket.org/logilab/pylint/issue/512/
"""
import os
import sys
import os.path
import subprocess
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
from scripts import utils
def main():
"""Main entry point.
Return:
The pylint exit status.
"""
utils.change_cwd()
files = []
for dirpath, _dirnames, filenames in os.walk('tests'):
for fn in filenames:
if os.path.splitext(fn)[1] == '.py':
files.append(os.path.join(dirpath, fn))
disabled = ['attribute-defined-outside-init', 'redefined-outer-name',
'unused-argument']
no_docstring_rgx = ['^__.*__$', '^setup$']
args = (['--disable={}'.format(','.join(disabled)),
'--no-docstring-rgx=({})'.format('|'.join(no_docstring_rgx))] +
sys.argv[1:] + files)
ret = subprocess.call(['pylint'] + args)
return ret
if __name__ == '__main__':
sys.exit(main())

View File

@ -23,46 +23,39 @@ Note that tests for parse_content_disposition are in their own
test_content_disposition.py file.
"""
import unittest
from qutebrowser.browser import http
from qutebrowser.test import stubs
class ParseContentTypeTests(unittest.TestCase):
class TestParseContentType:
"""Test for parse_content_type."""
def test_not_existing(self):
def test_not_existing(self, stubs):
"""Test without any Content-Type header."""
reply = stubs.FakeNetworkReply()
mimetype, rest = http.parse_content_type(reply)
self.assertIsNone(mimetype)
self.assertIsNone(rest)
assert mimetype is None
assert rest is None
def test_mimetype(self):
def test_mimetype(self, stubs):
"""Test with simple Content-Type header."""
reply = stubs.FakeNetworkReply(
headers={'Content-Type': 'image/example'})
mimetype, rest = http.parse_content_type(reply)
self.assertEqual(mimetype, 'image/example')
self.assertIsNone(rest)
assert mimetype == 'image/example'
assert rest is None
def test_empty(self):
def test_empty(self, stubs):
"""Test with empty Content-Type header."""
reply = stubs.FakeNetworkReply(headers={'Content-Type': ''})
mimetype, rest = http.parse_content_type(reply)
self.assertEqual(mimetype, '')
self.assertIsNone(rest)
assert mimetype == ''
assert rest is None
def test_additional(self):
def test_additional(self, stubs):
"""Test with Content-Type header with additional informations."""
reply = stubs.FakeNetworkReply(
headers={'Content-Type': 'image/example; encoding=UTF-8'})
mimetype, rest = http.parse_content_type(reply)
self.assertEqual(mimetype, 'image/example')
self.assertEqual(rest, ' encoding=UTF-8')
if __name__ == '__main__':
unittest.main()
assert mimetype == 'image/example'
assert rest == ' encoding=UTF-8'

View File

@ -0,0 +1,139 @@
# 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 webelement.tabhistory."""
from PyQt5.QtCore import QUrl, QPoint
import pytest
from qutebrowser.browser import tabhistory
from qutebrowser.browser.tabhistory import TabHistoryItem as Item
from qutebrowser.utils import qtutils
class TestSerializeHistory:
"""Tests for serialize()."""
ITEMS = [
Item(QUrl('https://www.heise.de/'), QUrl('http://www.heise.de/'),
'heise'),
Item(QUrl('http://example.com/%E2%80%A6'),
QUrl('http://example.com/%E2%80%A6'), 'percent', active=True),
Item(QUrl('http://example.com/?foo=bar'),
QUrl('http://original.url.example.com/'), 'arg',
user_data={'foo': 23, 'bar': 42}),
# From https://github.com/OtterBrowser/otter-browser/issues/709#issuecomment-74749471
Item(
QUrl('http://github.com/OtterBrowser/24/134/2344/otter-browser/'
'issues/709/'),
QUrl('http://github.com/OtterBrowser/24/134/2344/otter-browser/'
'issues/709/'),
'Page not found | github',
user_data={'zoom': 149, 'scroll-pos': QPoint(0, 0)}),
Item(
QUrl('https://mail.google.com/mail/u/0/#label/some+label/'
'234lkjsd0932lkjf884jqwerdf4'),
QUrl('https://mail.google.com/mail/u/0/#label/some+label/'
'234lkjsd0932lkjf884jqwerdf4'),
'"some label" - email@gmail.com - Gmail"',
user_data={'zoom': 120, 'scroll-pos': QPoint(0, 0)}),
]
@pytest.fixture(autouse=True)
def setup(self, webpage):
self.page = webpage
self.history = self.page.history()
assert self.history.count() == 0
stream, _data, self.user_data = tabhistory.serialize(self.ITEMS)
qtutils.deserialize_stream(stream, self.history)
def test_count(self):
"""Check if the history's count was loaded correctly."""
assert self.history.count() == len(self.ITEMS)
@pytest.mark.parametrize('i', range(len(ITEMS)))
def test_valid(self, i):
"""Check if all items are valid."""
assert self.history.itemAt(i).isValid()
@pytest.mark.parametrize('i', range(len(ITEMS)))
def test_no_userdata(self, i):
"""Check if all items have no user data."""
assert self.history.itemAt(i).userData() is None
def test_userdata(self):
"""Check if all user data has been restored to self.user_data."""
userdata_items = [item.user_data for item in self.ITEMS]
assert userdata_items == self.user_data
def test_currentitem(self):
"""Check if the current item index was loaded correctly."""
assert self.history.currentItemIndex() == 1
@pytest.mark.parametrize('i, item', enumerate(ITEMS))
def test_urls(self, i, item):
"""Check if the URLs were loaded correctly."""
assert self.history.itemAt(i).url() == item.url
@pytest.mark.parametrize('i, item', enumerate(ITEMS))
def test_original_urls(self, i, item):
"""Check if the original URLs were loaded correctly."""
assert self.history.itemAt(i).originalUrl() == item.original_url
@pytest.mark.parametrize('i, item', enumerate(ITEMS))
def test_titles(self, i, item):
"""Check if the titles were loaded correctly."""
assert self.history.itemAt(i).title() == item.title
class TestSerializeHistorySpecial:
"""Tests for serialize() without items set up in setup."""
@pytest.fixture(autouse=True)
def setup(self, webpage):
"""Set up the initial QWebPage for each test."""
self.page = webpage
self.history = self.page.history()
assert self.history.count() == 0
def test_no_active_item(self):
"""Check tabhistory.serialize with no active item."""
items = [Item(QUrl(), QUrl(), '')]
with pytest.raises(ValueError):
tabhistory.serialize(items)
def test_two_active_items(self):
"""Check tabhistory.serialize with two active items."""
items = [Item(QUrl(), QUrl(), '', active=True),
Item(QUrl(), QUrl(), ''),
Item(QUrl(), QUrl(), '', active=True)]
with pytest.raises(ValueError):
tabhistory.serialize(items)
def test_empty(self):
"""Check tabhistory.serialize with no items."""
items = []
stream, _data, user_data = tabhistory.serialize(items)
qtutils.deserialize_stream(stream, self.history)
assert self.history.count() == 0
assert self.history.currentItemIndex() == 0
assert not user_data

View File

@ -21,15 +21,14 @@
"""Tests for the webelement utils."""
import unittest
from unittest import mock
import collections.abc
from PyQt5.QtCore import QRect, QPoint
from PyQt5.QtWebKit import QWebElement
import pytest
from qutebrowser.browser import webelem
from qutebrowser.test import stubs
def get_webelem(geometry=None, frame=None, null=False, visibility='',
@ -87,17 +86,17 @@ def get_webelem(geometry=None, frame=None, null=False, visibility='',
return wrapped
class WebElementWrapperTests(unittest.TestCase):
class TestWebElementWrapper:
"""Test WebElementWrapper."""
def test_nullelem(self):
"""Test __init__ with a null element."""
with self.assertRaises(webelem.IsNullError):
with pytest.raises(webelem.IsNullError):
get_webelem(null=True)
class IsVisibleInvalidTests(unittest.TestCase):
class TestIsVisibleInvalid:
"""Tests for is_visible with invalid elements.
@ -105,7 +104,8 @@ class IsVisibleInvalidTests(unittest.TestCase):
frame: The FakeWebFrame we're using to test.
"""
def setUp(self):
@pytest.fixture(autouse=True)
def setup(self, stubs):
self.frame = stubs.FakeWebFrame(QRect(0, 0, 100, 100))
def test_nullelem(self):
@ -116,15 +116,15 @@ class IsVisibleInvalidTests(unittest.TestCase):
"""
elem = get_webelem()
elem._elem.isNull.return_value = True
with self.assertRaises(webelem.IsNullError):
with pytest.raises(webelem.IsNullError):
elem.is_visible(self.frame)
def test_invalid_invisible(self):
"""Test elements with an invalid geometry which are invisible."""
elem = get_webelem(QRect(0, 0, 0, 0), self.frame)
self.assertFalse(elem.geometry().isValid())
self.assertEqual(elem.geometry().x(), 0)
self.assertFalse(elem.is_visible(self.frame))
assert not elem.geometry().isValid()
assert elem.geometry().x() == 0
assert not elem.is_visible(self.frame)
def test_invalid_visible(self):
"""Test elements with an invalid geometry which are visible.
@ -133,11 +133,11 @@ class IsVisibleInvalidTests(unittest.TestCase):
which *are* visible, but don't have a valid geometry.
"""
elem = get_webelem(QRect(10, 10, 0, 0), self.frame)
self.assertFalse(elem.geometry().isValid())
self.assertTrue(elem.is_visible(self.frame))
assert not elem.geometry().isValid()
assert elem.is_visible(self.frame)
class IsVisibleScrollTests(unittest.TestCase):
class TestIsVisibleScroll:
"""Tests for is_visible when the frame is scrolled.
@ -145,22 +145,23 @@ class IsVisibleScrollTests(unittest.TestCase):
frame: The FakeWebFrame we're using to test.
"""
def setUp(self):
@pytest.fixture(autouse=True)
def setup(self, stubs):
self.frame = stubs.FakeWebFrame(QRect(0, 0, 100, 100),
scroll=QPoint(10, 10))
def test_invisible(self):
"""Test elements which should be invisible due to scrolling."""
elem = get_webelem(QRect(5, 5, 4, 4), self.frame)
self.assertFalse(elem.is_visible(self.frame))
assert not elem.is_visible(self.frame)
def test_visible(self):
"""Test elements which still should be visible after scrolling."""
elem = get_webelem(QRect(10, 10, 1, 1), self.frame)
self.assertTrue(elem.is_visible(self.frame))
assert elem.is_visible(self.frame)
class IsVisibleCssTests(unittest.TestCase):
class TestIsVisibleCss:
"""Tests for is_visible with CSS attributes.
@ -168,33 +169,34 @@ class IsVisibleCssTests(unittest.TestCase):
frame: The FakeWebFrame we're using to test.
"""
def setUp(self):
@pytest.fixture(autouse=True)
def setup(self, stubs):
self.frame = stubs.FakeWebFrame(QRect(0, 0, 100, 100))
def test_visibility_visible(self):
"""Check that elements with "visibility = visible" are visible."""
elem = get_webelem(QRect(0, 0, 10, 10), self.frame,
visibility='visible')
self.assertTrue(elem.is_visible(self.frame))
assert elem.is_visible(self.frame)
def test_visibility_hidden(self):
"""Check that elements with "visibility = hidden" are not visible."""
elem = get_webelem(QRect(0, 0, 10, 10), self.frame,
visibility='hidden')
self.assertFalse(elem.is_visible(self.frame))
assert not elem.is_visible(self.frame)
def test_display_inline(self):
"""Check that elements with "display = inline" are visible."""
elem = get_webelem(QRect(0, 0, 10, 10), self.frame, display='inline')
self.assertTrue(elem.is_visible(self.frame))
assert elem.is_visible(self.frame)
def test_display_none(self):
"""Check that elements with "display = none" are not visible."""
elem = get_webelem(QRect(0, 0, 10, 10), self.frame, display='none')
self.assertFalse(elem.is_visible(self.frame))
assert not elem.is_visible(self.frame)
class IsVisibleIframeTests(unittest.TestCase):
class TestIsVisibleIframe:
"""Tests for is_visible with a child frame.
@ -204,7 +206,8 @@ class IsVisibleIframeTests(unittest.TestCase):
elem1-elem4: FakeWebElements to test.
"""
def setUp(self):
@pytest.fixture(autouse=True)
def setup(self, stubs):
"""Set up the following base situation.
0, 0 300, 0
@ -236,64 +239,64 @@ class IsVisibleIframeTests(unittest.TestCase):
def test_not_scrolled(self):
"""Test base situation."""
self.assertTrue(self.frame.geometry().contains(self.iframe.geometry()))
self.assertTrue(self.elem1.is_visible(self.frame))
self.assertTrue(self.elem2.is_visible(self.frame))
self.assertFalse(self.elem3.is_visible(self.frame))
self.assertTrue(self.elem4.is_visible(self.frame))
assert self.frame.geometry().contains(self.iframe.geometry())
assert self.elem1.is_visible(self.frame)
assert self.elem2.is_visible(self.frame)
assert not self.elem3.is_visible(self.frame)
assert self.elem4.is_visible(self.frame)
def test_iframe_scrolled(self):
"""Scroll iframe down so elem3 gets visible and elem1/elem2 not."""
self.iframe.scrollPosition.return_value = QPoint(0, 100)
self.assertFalse(self.elem1.is_visible(self.frame))
self.assertFalse(self.elem2.is_visible(self.frame))
self.assertTrue(self.elem3.is_visible(self.frame))
self.assertTrue(self.elem4.is_visible(self.frame))
assert not self.elem1.is_visible(self.frame)
assert not self.elem2.is_visible(self.frame)
assert self.elem3.is_visible(self.frame)
assert self.elem4.is_visible(self.frame)
def test_mainframe_scrolled_iframe_visible(self):
"""Scroll mainframe down so iframe is partly visible but elem1 not."""
self.frame.scrollPosition.return_value = QPoint(0, 50)
geom = self.frame.geometry().translated(self.frame.scrollPosition())
self.assertFalse(geom.contains(self.iframe.geometry()))
self.assertTrue(geom.intersects(self.iframe.geometry()))
self.assertFalse(self.elem1.is_visible(self.frame))
self.assertTrue(self.elem2.is_visible(self.frame))
self.assertFalse(self.elem3.is_visible(self.frame))
self.assertTrue(self.elem4.is_visible(self.frame))
assert not geom.contains(self.iframe.geometry())
assert geom.intersects(self.iframe.geometry())
assert not self.elem1.is_visible(self.frame)
assert self.elem2.is_visible(self.frame)
assert not self.elem3.is_visible(self.frame)
assert self.elem4.is_visible(self.frame)
def test_mainframe_scrolled_iframe_invisible(self):
"""Scroll mainframe down so iframe is invisible."""
self.frame.scrollPosition.return_value = QPoint(0, 110)
geom = self.frame.geometry().translated(self.frame.scrollPosition())
self.assertFalse(geom.contains(self.iframe.geometry()))
self.assertFalse(geom.intersects(self.iframe.geometry()))
self.assertFalse(self.elem1.is_visible(self.frame))
self.assertFalse(self.elem2.is_visible(self.frame))
self.assertFalse(self.elem3.is_visible(self.frame))
self.assertTrue(self.elem4.is_visible(self.frame))
assert not geom.contains(self.iframe.geometry())
assert not geom.intersects(self.iframe.geometry())
assert not self.elem1.is_visible(self.frame)
assert not self.elem2.is_visible(self.frame)
assert not self.elem3.is_visible(self.frame)
assert self.elem4.is_visible(self.frame)
class IsWritableTests(unittest.TestCase):
class TestIsWritable:
"""Check is_writable."""
def test_writable(self):
"""Test a normal element."""
elem = get_webelem()
self.assertTrue(elem.is_writable())
assert elem.is_writable()
def test_disabled(self):
"""Test a disabled element."""
elem = get_webelem(attributes=['disabled'])
self.assertFalse(elem.is_writable())
assert not elem.is_writable()
def test_readonly(self):
"""Test a readonly element."""
elem = get_webelem(attributes=['readonly'])
self.assertFalse(elem.is_writable())
assert not elem.is_writable()
class JavascriptEscapeTests(unittest.TestCase):
class TestJavascriptEscape:
"""Check javascript_escape.
@ -301,33 +304,30 @@ class JavascriptEscapeTests(unittest.TestCase):
STRINGS: A list of (input, output) tuples.
"""
STRINGS = (
@pytest.mark.parametrize('before, after', [
('foo\\bar', r'foo\\bar'),
('foo\nbar', r'foo\nbar'),
("foo'bar", r"foo\'bar"),
('foo"bar', r'foo\"bar'),
)
def test_fake_escape(self):
])
def test_fake_escape(self, before, after):
"""Test javascript escaping."""
for before, after in self.STRINGS:
with self.subTest(before=before):
self.assertEqual(webelem.javascript_escape(before), after)
assert webelem.javascript_escape(before) == after
class GetChildFramesTests(unittest.TestCase):
class TestGetChildFrames:
"""Check get_child_frames."""
def test_single_frame(self):
def test_single_frame(self, stubs):
"""Test get_child_frames with a single frame without children."""
frame = stubs.FakeChildrenFrame()
children = webelem.get_child_frames(frame)
self.assertEqual(len(children), 1)
self.assertIs(children[0], frame)
assert len(children) == 1
assert children[0] is frame
frame.childFrames.assert_called_once_with()
def test_one_level(self):
def test_one_level(self, stubs):
r"""Test get_child_frames with one level of children.
o parent
@ -338,15 +338,15 @@ class GetChildFramesTests(unittest.TestCase):
child2 = stubs.FakeChildrenFrame()
parent = stubs.FakeChildrenFrame([child1, child2])
children = webelem.get_child_frames(parent)
self.assertEqual(len(children), 3)
self.assertIs(children[0], parent)
self.assertIs(children[1], child1)
self.assertIs(children[2], child2)
assert len(children) == 3
assert children[0] is parent
assert children[1] is child1
assert children[2] is child2
parent.childFrames.assert_called_once_with()
child1.childFrames.assert_called_once_with()
child2.childFrames.assert_called_once_with()
def test_multiple_levels(self):
def test_multiple_levels(self, stubs):
r"""Test get_child_frames with multiple levels of children.
o root
@ -360,189 +360,191 @@ class GetChildFramesTests(unittest.TestCase):
stubs.FakeChildrenFrame(second[2:4])]
root = stubs.FakeChildrenFrame(first)
children = webelem.get_child_frames(root)
self.assertEqual(len(children), 7)
self.assertIs(children[0], root)
assert len(children) == 7
assert children[0] is root
for frame in [root] + first + second:
with self.subTest(frame=frame):
frame.childFrames.assert_called_once_with()
frame.childFrames.assert_called_once_with()
class IsEditableTests(unittest.TestCase):
class TestIsEditable:
"""Tests for is_editable."""
def setUp(self):
@pytest.yield_fixture(autouse=True)
def setup(self):
old_config = webelem.config
webelem.config = None
yield
webelem.config = old_config
@pytest.fixture
def stub_config(self, stubs, mocker):
"""Fixture to create a config stub with an input section."""
config = stubs.ConfigStub({'input': {}})
mocker.patch('qutebrowser.browser.webelem.config', new=config)
return config
def test_input_plain(self):
"""Test with plain input element."""
elem = get_webelem(tagname='input')
self.assertTrue(elem.is_editable())
assert elem.is_editable()
def test_input_text(self):
"""Test with text input element."""
elem = get_webelem(tagname='input', attributes={'type': 'text'})
self.assertTrue(elem.is_editable())
assert elem.is_editable()
def test_input_text_caps(self):
"""Test with text input element with caps attributes."""
elem = get_webelem(tagname='INPUT', attributes={'TYPE': 'TEXT'})
self.assertTrue(elem.is_editable())
assert elem.is_editable()
def test_input_email(self):
"""Test with email input element."""
elem = get_webelem(tagname='input', attributes={'type': 'email'})
self.assertTrue(elem.is_editable())
assert elem.is_editable()
def test_input_url(self):
"""Test with url input element."""
elem = get_webelem(tagname='input', attributes={'type': 'url'})
self.assertTrue(elem.is_editable())
assert elem.is_editable()
def test_input_tel(self):
"""Test with tel input element."""
elem = get_webelem(tagname='input', attributes={'type': 'tel'})
self.assertTrue(elem.is_editable())
assert elem.is_editable()
def test_input_number(self):
"""Test with number input element."""
elem = get_webelem(tagname='input', attributes={'type': 'number'})
self.assertTrue(elem.is_editable())
assert elem.is_editable()
def test_input_password(self):
"""Test with password input element."""
elem = get_webelem(tagname='input', attributes={'type': 'password'})
self.assertTrue(elem.is_editable())
assert elem.is_editable()
def test_input_search(self):
"""Test with search input element."""
elem = get_webelem(tagname='input', attributes={'type': 'search'})
self.assertTrue(elem.is_editable())
assert elem.is_editable()
def test_input_button(self):
"""Button should not be editable."""
elem = get_webelem(tagname='input', attributes={'type': 'button'})
self.assertFalse(elem.is_editable())
assert not elem.is_editable()
def test_input_checkbox(self):
"""Checkbox should not be editable."""
elem = get_webelem(tagname='input', attributes={'type': 'checkbox'})
self.assertFalse(elem.is_editable())
assert not elem.is_editable()
def test_textarea(self):
"""Test textarea element."""
elem = get_webelem(tagname='textarea')
self.assertTrue(elem.is_editable())
assert elem.is_editable()
def test_select(self):
"""Test selectbox."""
elem = get_webelem(tagname='select')
self.assertFalse(elem.is_editable())
assert not elem.is_editable()
def test_input_disabled(self):
"""Test disabled input element."""
elem = get_webelem(tagname='input', attributes={'disabled': None})
self.assertFalse(elem.is_editable())
assert not elem.is_editable()
def test_input_readonly(self):
"""Test readonly input element."""
elem = get_webelem(tagname='input', attributes={'readonly': None})
self.assertFalse(elem.is_editable())
assert not elem.is_editable()
def test_textarea_disabled(self):
"""Test disabled textarea element."""
elem = get_webelem(tagname='textarea', attributes={'disabled': None})
self.assertFalse(elem.is_editable())
assert not elem.is_editable()
def test_textarea_readonly(self):
"""Test readonly textarea element."""
elem = get_webelem(tagname='textarea', attributes={'readonly': None})
self.assertFalse(elem.is_editable())
assert not elem.is_editable()
@mock.patch('qutebrowser.browser.webelem.config', new=stubs.ConfigStub(
{'input': {'insert-mode-on-plugins': True}}))
def test_embed_true(self):
def test_embed_true(self, stub_config):
"""Test embed-element with insert-mode-on-plugins true."""
stub_config.data['input']['insert-mode-on-plugins'] = True
elem = get_webelem(tagname='embed')
self.assertTrue(elem.is_editable())
assert elem.is_editable()
@mock.patch('qutebrowser.browser.webelem.config', new=stubs.ConfigStub(
{'input': {'insert-mode-on-plugins': True}}))
def test_applet_true(self):
def test_applet_true(self, stub_config):
"""Test applet-element with insert-mode-on-plugins true."""
stub_config.data['input']['insert-mode-on-plugins'] = True
elem = get_webelem(tagname='applet')
self.assertTrue(elem.is_editable())
assert elem.is_editable()
@mock.patch('qutebrowser.browser.webelem.config', new=stubs.ConfigStub(
{'input': {'insert-mode-on-plugins': False}}))
def test_embed_false(self):
def test_embed_false(self, stub_config):
"""Test embed-element with insert-mode-on-plugins false."""
stub_config.data['input']['insert-mode-on-plugins'] = False
elem = get_webelem(tagname='embed')
self.assertFalse(elem.is_editable())
assert not elem.is_editable()
@mock.patch('qutebrowser.browser.webelem.config', new=stubs.ConfigStub(
{'input': {'insert-mode-on-plugins': False}}))
def test_applet_false(self):
def test_applet_false(self, stub_config):
"""Test applet-element with insert-mode-on-plugins false."""
stub_config.data['input']['insert-mode-on-plugins'] = False
elem = get_webelem(tagname='applet')
self.assertFalse(elem.is_editable())
assert not elem.is_editable()
def test_object_no_type(self):
"""Test object-element without type."""
elem = get_webelem(tagname='object')
self.assertFalse(elem.is_editable())
assert not elem.is_editable()
def test_object_image(self):
"""Test object-element with image type."""
elem = get_webelem(tagname='object', attributes={'type': 'image/gif'})
self.assertFalse(elem.is_editable())
assert not elem.is_editable()
@mock.patch('qutebrowser.browser.webelem.config', new=stubs.ConfigStub(
{'input': {'insert-mode-on-plugins': True}}))
def test_object_application(self):
def test_object_application(self, stub_config):
"""Test object-element with application type."""
stub_config.data['input']['insert-mode-on-plugins'] = True
elem = get_webelem(tagname='object',
attributes={'type': 'application/foo'})
self.assertTrue(elem.is_editable())
assert elem.is_editable()
@mock.patch('qutebrowser.browser.webelem.config', new=stubs.ConfigStub(
{'input': {'insert-mode-on-plugins': False}}))
def test_object_application_false(self):
def test_object_application_false(self, stub_config):
"""Test object-element with application type but not ...-on-plugins."""
stub_config.data['input']['insert-mode-on-plugins'] = False
elem = get_webelem(tagname='object',
attributes={'type': 'application/foo'})
self.assertFalse(elem.is_editable())
assert not elem.is_editable()
@mock.patch('qutebrowser.browser.webelem.config', new=stubs.ConfigStub(
{'input': {'insert-mode-on-plugins': True}}))
def test_object_classid(self):
def test_object_classid(self, stub_config):
"""Test object-element with classid."""
stub_config.data['input']['insert-mode-on-plugins'] = True
elem = get_webelem(tagname='object',
attributes={'type': 'foo', 'classid': 'foo'})
self.assertTrue(elem.is_editable())
assert elem.is_editable()
@mock.patch('qutebrowser.browser.webelem.config', new=stubs.ConfigStub(
{'input': {'insert-mode-on-plugins': False}}))
def test_object_classid_false(self):
def test_object_classid_false(self, stub_config):
"""Test object-element with classid but not insert-mode-on-plugins."""
stub_config.data['input']['insert-mode-on-plugins'] = False
elem = get_webelem(tagname='object',
attributes={'type': 'foo', 'classid': 'foo'})
self.assertFalse(elem.is_editable())
assert not elem.is_editable()
def test_div_empty(self):
"""Test div-element without class."""
elem = get_webelem(tagname='div')
self.assertFalse(elem.is_editable())
assert not elem.is_editable()
def test_div_noneditable(self):
"""Test div-element with non-editable class."""
elem = get_webelem(tagname='div', classes='foo-kix-bar')
self.assertFalse(elem.is_editable())
assert not elem.is_editable()
def test_div_xik(self):
"""Test div-element with xik class."""
elem = get_webelem(tagname='div', classes='foo kix-foo')
self.assertTrue(elem.is_editable())
assert elem.is_editable()
def test_div_xik_caps(self):
"""Test div-element with xik class in caps.
@ -550,13 +552,9 @@ class IsEditableTests(unittest.TestCase):
This tests if classes are case sensitive as they should.
"""
elem = get_webelem(tagname='div', classes='KIX-FOO')
self.assertFalse(elem.is_editable())
assert not elem.is_editable()
def test_div_codemirror(self):
"""Test div-element with codemirror class."""
elem = get_webelem(tagname='div', classes='foo CodeMirror-foo')
self.assertTrue(elem.is_editable())
if __name__ == '__main__':
unittest.main()
assert elem.is_editable()

View File

@ -22,27 +22,25 @@
import os
import os.path
import unittest
import configparser
import tempfile
import types
import shutil
import argparse
from unittest import mock
from PyQt5.QtCore import QObject
from PyQt5.QtGui import QColor
import pytest
from qutebrowser.config import config, configexc
from qutebrowser.test import helpers
from qutebrowser.utils import objreg, standarddir
class ConfigParserTests(unittest.TestCase):
class TestConfigParser:
"""Test reading of ConfigParser."""
def setUp(self):
@pytest.fixture(autouse=True)
def setup(self):
self.cp = configparser.ConfigParser(interpolation=None,
comment_prefixes='#')
self.cp.optionxform = lambda opt: opt # be case-insensitive
@ -52,43 +50,41 @@ class ConfigParserTests(unittest.TestCase):
"""Test a simple option which is not transformed."""
self.cp.read_dict({'general': {'ignore-case': 'false'}})
self.cfg._from_cp(self.cp)
self.assertFalse(self.cfg.get('general', 'ignore-case'))
assert not self.cfg.get('general', 'ignore-case')
def test_transformed_section_old(self):
"""Test a transformed section with the old name."""
self.cp.read_dict({'permissions': {'allow-plugins': 'true'}})
self.cfg._from_cp(self.cp)
self.assertTrue(self.cfg.get('content', 'allow-plugins'))
assert self.cfg.get('content', 'allow-plugins')
def test_transformed_section_new(self):
"""Test a transformed section with the new name."""
self.cp.read_dict({'content': {'allow-plugins': 'true'}})
self.cfg._from_cp(self.cp)
self.assertTrue(self.cfg.get('content', 'allow-plugins'))
assert self.cfg.get('content', 'allow-plugins')
def test_transformed_option_old(self):
"""Test a transformed option with the old name."""
# WORKAROUND for unknown PyQt bug
# Instance of 'str' has no 'name' member
self.cp.read_dict({'colors': {'tab.fg.odd': 'pink'}})
self.cfg._from_cp(self.cp)
self.assertEqual(self.cfg.get('colors', 'tabs.fg.odd').name(),
QColor('pink').name())
actual = self.cfg.get('colors', 'tabs.fg.odd').name()
expected = QColor('pink').name()
assert actual == expected
def test_transformed_option_new(self):
"""Test a transformed section with the new name."""
# WORKAROUND for unknown PyQt bug
# Instance of 'str' has no 'name' member
self.cp.read_dict({'colors': {'tabs.fg.odd': 'pink'}})
self.cfg._from_cp(self.cp)
self.assertEqual(self.cfg.get('colors', 'tabs.fg.odd').name(),
QColor('pink').name())
actual = self.cfg.get('colors', 'tabs.fg.odd').name()
expected = QColor('pink').name()
assert actual == expected
def test_invalid_value(self):
"""Test setting an invalid value."""
self.cp.read_dict({'general': {'ignore-case': 'invalid'}})
self.cfg._from_cp(self.cp)
with self.assertRaises(configexc.ValidationError):
with pytest.raises(configexc.ValidationError):
self.cfg._validate_all()
def test_invalid_value_interpolated(self):
@ -96,7 +92,7 @@ class ConfigParserTests(unittest.TestCase):
self.cp.read_dict({'general': {'ignore-case': 'smart',
'wrap-search': '${ignore-case}'}})
self.cfg._from_cp(self.cp)
with self.assertRaises(configexc.ValidationError):
with pytest.raises(configexc.ValidationError):
self.cfg._validate_all()
def test_interpolation(self):
@ -104,8 +100,8 @@ class ConfigParserTests(unittest.TestCase):
self.cp.read_dict({'general': {'ignore-case': 'false',
'wrap-search': '${ignore-case}'}})
self.cfg._from_cp(self.cp)
self.assertFalse(self.cfg.get('general', 'ignore-case'))
self.assertFalse(self.cfg.get('general', 'wrap-search'))
assert not self.cfg.get('general', 'ignore-case')
assert not self.cfg.get('general', 'wrap-search')
def test_interpolation_cross_section(self):
"""Test setting an interpolated value from another section."""
@ -116,50 +112,50 @@ class ConfigParserTests(unittest.TestCase):
}
)
self.cfg._from_cp(self.cp)
self.assertFalse(self.cfg.get('general', 'ignore-case'))
self.assertFalse(self.cfg.get('network', 'do-not-track'))
assert not self.cfg.get('general', 'ignore-case')
assert not self.cfg.get('network', 'do-not-track')
def test_invalid_interpolation(self):
"""Test an invalid interpolation."""
self.cp.read_dict({'general': {'ignore-case': '${foo}'}})
self.cfg._from_cp(self.cp)
with self.assertRaises(configparser.InterpolationError):
with pytest.raises(configparser.InterpolationError):
self.cfg._validate_all()
def test_invalid_interpolation_syntax(self):
"""Test an invalid interpolation syntax."""
self.cp.read_dict({'general': {'ignore-case': '${'}})
with self.assertRaises(configexc.InterpolationSyntaxError):
with pytest.raises(configexc.InterpolationSyntaxError):
self.cfg._from_cp(self.cp)
def test_invalid_section(self):
"""Test an invalid section."""
self.cp.read_dict({'foo': {'bar': 'baz'}})
with self.assertRaises(configexc.NoSectionError):
with pytest.raises(configexc.NoSectionError):
self.cfg._from_cp(self.cp)
def test_invalid_option(self):
"""Test an invalid option."""
self.cp.read_dict({'general': {'bar': 'baz'}})
with self.assertRaises(configexc.NoOptionError):
with pytest.raises(configexc.NoOptionError):
self.cfg._from_cp(self.cp)
def test_invalid_section_relaxed(self):
"""Test an invalid section with relaxed=True."""
self.cp.read_dict({'foo': {'bar': 'baz'}})
self.cfg._from_cp(self.cp, relaxed=True)
with self.assertRaises(configexc.NoSectionError):
with pytest.raises(configexc.NoSectionError):
self.cfg.get('foo', 'bar') # pylint: disable=bad-config-call
def test_invalid_option_relaxed(self):
"""Test an invalid option with relaxed=True."""
self.cp.read_dict({'general': {'bar': 'baz'}})
self.cfg._from_cp(self.cp, relaxed=True)
with self.assertRaises(configexc.NoOptionError):
with pytest.raises(configexc.NoOptionError):
self.cfg.get('general', 'bar') # pylint: disable=bad-config-call
class DefaultConfigTests(unittest.TestCase):
class TestDefaultConfig:
"""Test validating of the default config."""
@ -169,40 +165,32 @@ class DefaultConfigTests(unittest.TestCase):
conf._validate_all()
class ConfigInitTests(unittest.TestCase):
class TestConfigInit:
"""Test initializing of the config."""
def setUp(self):
self.temp_dir = tempfile.mkdtemp()
self.conf_path = os.path.join(self.temp_dir, 'config')
self.data_path = os.path.join(self.temp_dir, 'data')
self.cache_path = os.path.join(self.temp_dir, 'cache')
os.mkdir(self.conf_path)
os.mkdir(self.data_path)
os.mkdir(self.cache_path)
@pytest.yield_fixture(autouse=True)
def setup(self, tmpdir):
self.conf_path = (tmpdir / 'config').ensure(dir=1)
self.data_path = (tmpdir / 'data').ensure(dir=1)
self.cache_path = (tmpdir / 'cache').ensure(dir=1)
self.env = {
'XDG_CONFIG_HOME': self.conf_path,
'XDG_DATA_HOME': self.data_path,
'XDG_CACHE_HOME': self.cache_path,
'XDG_CONFIG_HOME': str(self.conf_path),
'XDG_DATA_HOME': str(self.data_path),
'XDG_CACHE_HOME': str(self.cache_path),
}
objreg.register('app', QObject())
objreg.register('save-manager', mock.MagicMock())
args = argparse.Namespace(relaxed_config=False)
objreg.register('args', args)
def tearDown(self):
shutil.rmtree(self.temp_dir)
yield
objreg.global_registry.clear()
def test_config_none(self):
def test_config_none(self, monkeypatch):
"""Test initializing with config path set to None."""
args = types.SimpleNamespace(confdir='')
with helpers.environ_set_temp(self.env):
standarddir.init(args)
config.init()
self.assertFalse(os.listdir(self.conf_path))
if __name__ == '__main__':
unittest.main()
for k, v in self.env.items():
monkeypatch.setenv(k, v)
standarddir.init(args)
config.init()
assert not os.listdir(str(self.conf_path))

78
tests/conftest.py Normal file
View File

@ -0,0 +1,78 @@
# 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/>.
"""The qutebrowser test suite contest file."""
import pytest
@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.fixture(scope='session')
def stubs():
"""Provide access to stub objects useful for testing."""
import stubs
return stubs
@pytest.fixture(scope='session')
def unicode_encode_err():
"""Provide a fake UnicodeEncodeError exception."""
return UnicodeEncodeError('ascii', # codec
'', # object
0, # start
2, # end
'fake exception') # reason
@pytest.fixture
def webpage():
"""Get a new QWebPage object."""
from PyQt5.QtWebKitWidgets import QWebPage
from PyQt5.QtNetwork import QNetworkAccessManager
page = QWebPage()
nam = page.networkAccessManager()
nam.setNetworkAccessible(QNetworkAccessManager.NotAccessible)
return page
@pytest.fixture
def fake_keyevent_factory():
"""Fixture that when called will return a mock instance of a QKeyEvent."""
from unittest import mock
from PyQt5.QtGui import QKeyEvent
def fake_keyevent(key, modifiers=0, text=''):
"""Generate a new fake QKeyPressEvent."""
evtmock = mock.create_autospec(QKeyEvent, instance=True)
evtmock.key.return_value = key
evtmock.modifiers.return_value = modifiers
evtmock.text.return_value = text
return evtmock
return fake_keyevent

View File

@ -0,0 +1,289 @@
# 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/>.
# pylint: disable=protected-access
"""Tests for BaseKeyParser."""
import logging
from unittest import mock
from PyQt5.QtCore import Qt
import pytest
from qutebrowser.keyinput import basekeyparser
from qutebrowser.utils import objreg, log
CONFIG = {'input': {'timeout': 100}}
BINDINGS = {'test': {'<Ctrl-a>': 'ctrla',
'a': 'a',
'ba': 'ba',
'ax': 'ax',
'ccc': 'ccc'},
'test2': {'foo': 'bar', '<Ctrl+X>': 'ctrlx'}}
@pytest.yield_fixture
def fake_keyconfig():
"""Create a mock of a KeyConfiguration and register it into objreg."""
fake_keyconfig = mock.Mock(spec=['get_bindings_for'])
fake_keyconfig.get_bindings_for.side_effect = lambda s: BINDINGS[s]
objreg.register('key-config', fake_keyconfig)
yield
objreg.delete('key-config')
@pytest.fixture
def mock_timer(mocker, stubs):
"""Mock the Timer class used by the usertypes module with a stub."""
mocker.patch('qutebrowser.keyinput.basekeyparser.usertypes.Timer',
new=stubs.FakeTimer)
class TestSplitCount:
"""Test the _split_count method.
Attributes:
kp: The BaseKeyParser we're testing.
"""
@pytest.fixture(autouse=True)
def setup(self):
self.kp = basekeyparser.BaseKeyParser(0, supports_count=True)
def test_onlycount(self):
"""Test split_count with only a count."""
self.kp._keystring = '10'
assert self.kp._split_count() == (10, '')
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')
class TestReadConfig:
"""Test reading the config."""
def test_read_config_invalid(self):
"""Test reading config without setting it before."""
kp = basekeyparser.BaseKeyParser(0)
with pytest.raises(ValueError):
kp.read_config()
def test_read_config_valid(self):
"""Test reading config."""
kp = basekeyparser.BaseKeyParser(0, supports_count=True,
supports_chains=True)
kp.read_config('test')
assert 'ccc' in kp.bindings
assert 'ctrl+a' in kp.special_bindings
kp.read_config('test2')
assert 'ccc' not in kp.bindings
assert 'ctrl+a' not in kp.special_bindings
assert 'foo' in kp.bindings
assert 'ctrl+x' in kp.special_bindings
@pytest.mark.usefixtures('mock_timer')
class TestSpecialKeys:
"""Check execute() with special keys.
Attributes:
kp: The BaseKeyParser to be tested.
"""
@pytest.fixture(autouse=True)
def setup(self, caplog, fake_keyconfig):
self.kp = basekeyparser.BaseKeyParser(0)
self.kp.execute = mock.Mock()
with caplog.atLevel(logging.WARNING, log.keyboard.name):
# Ignoring keychain 'ccc' in mode 'test' because keychains are not
# supported there.
self.kp.read_config('test')
def test_valid_key(self, fake_keyevent_factory):
"""Test a valid special keyevent."""
self.kp.handle(fake_keyevent_factory(Qt.Key_A, Qt.ControlModifier))
self.kp.handle(fake_keyevent_factory(Qt.Key_X, Qt.ControlModifier))
self.kp.execute.assert_called_once_with('ctrla', self.kp.Type.special)
def test_invalid_key(self, fake_keyevent_factory):
"""Test an invalid special keyevent."""
self.kp.handle(fake_keyevent_factory(Qt.Key_A, (Qt.ControlModifier |
Qt.AltModifier)))
assert not self.kp.execute.called
def test_keychain(self, fake_keyevent_factory):
"""Test a keychain."""
self.kp.handle(fake_keyevent_factory(Qt.Key_B))
self.kp.handle(fake_keyevent_factory(Qt.Key_A))
assert not self.kp.execute.called
@pytest.mark.usefixtures('mock_timer')
class TestKeyChain:
"""Test execute() with keychain support.
Attributes:
kp: The BaseKeyParser to be tested.
"""
@pytest.fixture(autouse=True)
def setup(self, fake_keyconfig):
"""Set up mocks and read the test config."""
self.kp = basekeyparser.BaseKeyParser(0, supports_chains=True,
supports_count=False)
self.kp.execute = mock.Mock()
self.kp.read_config('test')
def test_valid_special_key(self, fake_keyevent_factory):
"""Test valid special key."""
self.kp.handle(fake_keyevent_factory(Qt.Key_A, Qt.ControlModifier))
self.kp.handle(fake_keyevent_factory(Qt.Key_X, Qt.ControlModifier))
self.kp.execute.assert_called_once_with('ctrla', self.kp.Type.special)
assert self.kp._keystring == ''
def test_invalid_special_key(self, fake_keyevent_factory):
"""Test invalid special key."""
self.kp.handle(fake_keyevent_factory(Qt.Key_A, (Qt.ControlModifier |
Qt.AltModifier)))
assert not self.kp.execute.called
assert self.kp._keystring == ''
def test_keychain(self, fake_keyevent_factory):
"""Test valid keychain."""
# Press 'x' which is ignored because of no match
self.kp.handle(fake_keyevent_factory(Qt.Key_X, text='x'))
# Then start the real chain
self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='b'))
self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='a'))
self.kp.execute.assert_called_once_with('ba', self.kp.Type.chain, None)
assert self.kp._keystring == ''
def test_ambiguous_keychain(self, fake_keyevent_factory, mocker, stubs):
"""Test ambiguous keychain."""
mocker.patch('qutebrowser.keyinput.basekeyparser.config',
new=stubs.ConfigStub(CONFIG))
timer = self.kp._ambiguous_timer
assert not timer.isActive()
# We start with 'a' where the keychain gives us an ambiguous result.
# Then we check if the timer has been set up correctly
self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='a'))
assert not self.kp.execute.called
assert timer.isSingleShot()
assert timer.interval() == 100
assert timer.isActive()
# Now we type an 'x' and check 'ax' has been executed and the timer
# stopped.
self.kp.handle(fake_keyevent_factory(Qt.Key_X, text='x'))
self.kp.execute.assert_called_once_with('ax', self.kp.Type.chain, None)
assert not timer.isActive()
assert self.kp._keystring == ''
def test_invalid_keychain(self, fake_keyevent_factory):
"""Test invalid keychain."""
self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='b'))
self.kp.handle(fake_keyevent_factory(Qt.Key_C, text='c'))
assert self.kp._keystring == ''
@pytest.mark.usefixtures('mock_timer')
class TestCount:
"""Test execute() with counts."""
@pytest.fixture(autouse=True)
def setup(self, fake_keyconfig):
self.kp = basekeyparser.BaseKeyParser(0, supports_chains=True,
supports_count=True)
self.kp.execute = mock.Mock()
self.kp.read_config('test')
def test_no_count(self, fake_keyevent_factory):
"""Test with no count added."""
self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='b'))
self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='a'))
self.kp.execute.assert_called_once_with('ba', self.kp.Type.chain, None)
assert self.kp._keystring == ''
def test_count_0(self, fake_keyevent_factory):
"""Test with count=0."""
self.kp.handle(fake_keyevent_factory(Qt.Key_0, text='0'))
self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='b'))
self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='a'))
self.kp.execute.assert_called_once_with('ba', self.kp.Type.chain, 0)
assert self.kp._keystring == ''
def test_count_42(self, fake_keyevent_factory):
"""Test with count=42."""
self.kp.handle(fake_keyevent_factory(Qt.Key_4, text='4'))
self.kp.handle(fake_keyevent_factory(Qt.Key_2, text='2'))
self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='b'))
self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='a'))
self.kp.execute.assert_called_once_with('ba', self.kp.Type.chain, 42)
assert self.kp._keystring == ''
def test_count_42_invalid(self, fake_keyevent_factory):
"""Test with count=42 and invalid command."""
# Invalid call with ccx gets ignored
self.kp.handle(fake_keyevent_factory(Qt.Key_4, text='4'))
self.kp.handle(fake_keyevent_factory(Qt.Key_2, text='2'))
self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='c'))
self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='c'))
self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='x'))
assert not self.kp.execute.called
assert self.kp._keystring == ''
# Valid call with ccc gets the correct count
self.kp.handle(fake_keyevent_factory(Qt.Key_4, text='2'))
self.kp.handle(fake_keyevent_factory(Qt.Key_2, text='3'))
self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='c'))
self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='c'))
self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='c'))
self.kp.execute.assert_called_once_with('ccc', self.kp.Type.chain, 23)
assert self.kp._keystring == ''

View File

@ -21,11 +21,10 @@
from PyQt5.QtCore import Qt
import unittest
from unittest import mock
import pytest
from qutebrowser.keyinput import modeparsers
from qutebrowser.test import stubs, helpers
from qutebrowser.utils import objreg
@ -39,11 +38,7 @@ fake_keyconfig = mock.Mock(spec=['get_bindings_for'])
fake_keyconfig.get_bindings_for.side_effect = lambda s: BINDINGS[s]
@mock.patch('qutebrowser.keyinput.basekeyparser.usertypes.Timer',
new=stubs.FakeTimer)
@mock.patch('qutebrowser.keyinput.modeparsers.config',
new=stubs.ConfigStub(CONFIG))
class NormalKeyParserTests(unittest.TestCase):
class TestsNormalKeyParser:
"""Tests for NormalKeyParser.
@ -53,46 +48,47 @@ class NormalKeyParserTests(unittest.TestCase):
# pylint: disable=protected-access
def setUp(self):
@pytest.yield_fixture(autouse=True)
def setup(self, mocker, stubs):
"""Set up mocks and read the test config."""
mocker.patch('qutebrowser.keyinput.basekeyparser.usertypes.Timer',
new=stubs.FakeTimer)
mocker.patch('qutebrowser.keyinput.modeparsers.config',
new=stubs.ConfigStub(CONFIG))
objreg.register('key-config', fake_keyconfig)
self.kp = modeparsers.NormalKeyParser(0)
self.kp.execute = mock.Mock()
def tearDown(self):
yield
objreg.delete('key-config')
def test_keychain(self):
def test_keychain(self, fake_keyevent_factory):
"""Test valid keychain."""
# Press 'x' which is ignored because of no match
self.kp.handle(helpers.fake_keyevent(Qt.Key_X, text='x'))
self.kp.handle(fake_keyevent_factory(Qt.Key_X, text='x'))
# Then start the real chain
self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='b'))
self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='a'))
self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='b'))
self.kp.handle(fake_keyevent_factory(Qt.Key_A, text='a'))
self.kp.execute.assert_called_once_with('ba', self.kp.Type.chain, None)
self.assertEqual(self.kp._keystring, '')
assert self.kp._keystring == ''
def test_partial_keychain_timeout(self):
def test_partial_keychain_timeout(self, fake_keyevent_factory):
"""Test partial keychain timeout."""
timer = self.kp._partial_timer
self.assertFalse(timer.isActive())
assert not timer.isActive()
# Press 'b' for a partial match.
# Then we check if the timer has been set up correctly
self.kp.handle(helpers.fake_keyevent(Qt.Key_B, text='b'))
self.assertTrue(timer.isSingleShot())
self.assertEqual(timer.interval(), 100)
self.assertTrue(timer.isActive())
self.kp.handle(fake_keyevent_factory(Qt.Key_B, text='b'))
assert timer.isSingleShot()
assert timer.interval() == 100
assert timer.isActive()
self.assertFalse(self.kp.execute.called)
self.assertEqual(self.kp._keystring, 'b')
assert not self.kp.execute.called
assert self.kp._keystring == 'b'
# Now simulate a timeout and check the keystring has been cleared.
keystring_updated_mock = mock.Mock()
self.kp.keystring_updated.connect(keystring_updated_mock)
timer.timeout.emit()
self.assertFalse(self.kp.execute.called)
self.assertEqual(self.kp._keystring, '')
assert not self.kp.execute.called
assert self.kp._keystring == ''
keystring_updated_mock.assert_called_once_with('')
if __name__ == '__main__':
unittest.main()

View File

@ -0,0 +1,52 @@
# 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/>.
"""pytest fixtures and utilities for testing.
Fixtures defined here will be visible to all test files in this directory and
below.
"""
import pytest
from qutebrowser.config.config import ConfigManager
from qutebrowser.utils import objreg
@pytest.yield_fixture
def default_config():
"""
Fixture that registers an empty config object into the objreg module.
Should be used by tests which create widgets that obtain their initial
state from the global config object.
Note:
If we declare this fixture like this:
@pytest.yield_fixture(autouse=True)
Then all tests below this file will have a default config registered
and ready for use. Is that desirable?
"""
config_obj = ConfigManager(configdir=None, fname=None, relaxed=True)
objreg.register('config', config_obj)
yield config_obj
objreg.delete('config')

View File

@ -17,19 +17,32 @@
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""The qutebrowser test suite."""
import atexit
"""Test Percentage widget."""
from PyQt5.QtWidgets import QApplication
import pytest
from qutebrowser.test import log
from qutebrowser.mainwindow.statusbar.percentage import Percentage
# We create a singleton QApplication here.
qApp = QApplication([])
qApp.setApplicationName('qutebrowser')
qApp.processEvents()
atexit.register(qApp.processEvents)
atexit.register(qApp.quit)
log.init()
@pytest.mark.parametrize('y, expected', [
(0, '[top]'),
(100, '[bot]'),
(75, '[75%]'),
(25, '[25%]'),
(5, '[ 5%]'),
])
def test_percentage_text(qtbot, y, expected):
"""
Test text displayed by the widget based on the y position of a page.
Args:
qtbot: pytestqt.plugin.QtBot fixture
y: y position of the page as an int in the range [0, 100].
parametrized.
expected: expected text given y position. parametrized.
"""
percentage = Percentage()
qtbot.add_widget(percentage)
percentage.set_perc(None, y=y)
assert percentage.text() == expected

View File

@ -0,0 +1,74 @@
# 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 Progress widget."""
from collections import namedtuple
import pytest
from qutebrowser.browser import webview
from qutebrowser.mainwindow.statusbar.progress import Progress
@pytest.fixture
def progress_widget(qtbot, default_config):
"""Create a Progress widget and checks its initial state."""
widget = Progress()
qtbot.add_widget(widget)
assert not widget.isVisible()
assert not widget.isTextVisible()
return widget
def test_load_started(progress_widget):
"""Ensure the Progress widget reacts properly when the page starts loading.
Args:
progress_widget: Progress widget that will be tested.
"""
progress_widget.on_load_started()
assert progress_widget.value() == 0
assert progress_widget.isVisible()
# mock tab object
Tab = namedtuple('Tab', 'progress load_status')
@pytest.mark.parametrize('tab, expected_visible', [
(Tab(15, webview.LoadStatus.loading), True),
(Tab(100, webview.LoadStatus.success), False),
(Tab(100, webview.LoadStatus.error), False),
(Tab(100, webview.LoadStatus.warn), False),
(Tab(100, webview.LoadStatus.none), False),
])
def test_tab_changed(progress_widget, tab, expected_visible):
"""Test that progress widget value and visibility state match expectations.
This uses a dummy Tab object.
Args:
progress_widget: Progress widget that will be tested.
"""
progress_widget.on_tab_changed(tab)
actual = progress_widget.value(), progress_widget.isVisible()
expected = tab.progress, expected_visible
assert actual == expected

View File

@ -17,4 +17,27 @@
# 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 qutebrowser.utils package."""
"""Test TextBase widget."""
from qutebrowser.mainwindow.statusbar.textbase import TextBase
def test_elided_text(qtbot):
"""Ensure that a widget too small to hold the entire label text will elide.
Note:
It is difficult to check what is actually being drawn in a portable
way, so at least we ensure our customized methods are being called and
the elided string contains the horizontal ellipsis character.
Args:
qtbot: pytestqt.plugin.QtBot fixture
"""
label = TextBase()
qtbot.add_widget(label)
long_string = 'Hello world! ' * 20
label.setText(long_string)
label.resize(100, 50)
label.show()
assert '' in label._elided_text # pylint: disable=protected-access

View File

@ -23,19 +23,16 @@
import os
import os.path
import unittest
import logging
from unittest import mock
from PyQt5.QtCore import QProcess
import pytest
from qutebrowser.misc import editor
from qutebrowser.test import stubs, helpers
@mock.patch('qutebrowser.misc.editor.QProcess',
new_callable=stubs.FakeQProcess)
class ArgTests(unittest.TestCase):
class TestArg:
"""Test argument handling.
@ -43,52 +40,48 @@ class ArgTests(unittest.TestCase):
editor: The ExternalEditor instance to test.
"""
def setUp(self):
@pytest.yield_fixture(autouse=True)
def setup(self, mocker, stubs):
mocker.patch('qutebrowser.misc.editor.QProcess',
new_callable=stubs.FakeQProcess)
self.config = stubs.ConfigStub()
mocker.patch('qutebrowser.misc.editor.config', new=self.config)
self.editor = editor.ExternalEditor(0)
yield
self.editor._cleanup() # pylint: disable=protected-access
@mock.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub(
{'general': {'editor': ['bin'], 'editor-encoding': 'utf-8'}}))
def test_simple_start_args(self, _proc_mock):
def test_simple_start_args(self):
"""Test starting editor without arguments."""
self.config.data = {
'general': {'editor': ['bin'], 'editor-encoding': 'utf-8'}}
self.editor.edit("")
self.editor._proc.start.assert_called_with("bin", [])
@mock.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub(
{'general': {'editor': ['bin', 'foo', 'bar'],
'editor-encoding': 'utf-8'}}))
def test_start_args(self, _proc_mock):
def test_start_args(self):
"""Test starting editor with static arguments."""
self.config.data = {'general': {'editor': ['bin', 'foo', 'bar'],
'editor-encoding': 'utf-8'}}
self.editor.edit("")
self.editor._proc.start.assert_called_with("bin", ["foo", "bar"])
@mock.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub(
{'general': {'editor': ['bin', 'foo', '{}', 'bar'],
'editor-encoding': 'utf-8'}}))
def test_placeholder(self, _proc_mock):
def test_placeholder(self):
"""Test starting editor with placeholder argument."""
self.config.data = {'general': {'editor': ['bin', 'foo', '{}', 'bar'],
'editor-encoding': 'utf-8'}}
self.editor.edit("")
filename = self.editor._filename
self.editor._proc.start.assert_called_with(
"bin", ["foo", filename, "bar"])
@mock.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub(
{'general': {'editor': ['bin', 'foo{}bar'],
'editor-encoding': 'utf-8'}}))
def test_in_arg_placeholder(self, _proc_mock):
def test_in_arg_placeholder(self):
"""Test starting editor with placeholder argument inside argument."""
self.config.data = {'general': {'editor': ['bin', 'foo{}bar'],
'editor-encoding': 'utf-8'}}
self.editor.edit("")
self.editor._proc.start.assert_called_with("bin", ["foo{}bar"])
def tearDown(self):
self.editor._cleanup() # pylint: disable=protected-access
@mock.patch('qutebrowser.misc.editor.message', new=helpers.MessageModule())
@mock.patch('qutebrowser.misc.editor.QProcess',
new_callable=stubs.FakeQProcess)
@mock.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub(
{'general': {'editor': [''], 'editor-encoding': 'utf-8'}}))
class FileHandlingTests(unittest.TestCase):
class TestFileHandling:
"""Test creation/deletion of tempfile.
@ -96,42 +89,49 @@ class FileHandlingTests(unittest.TestCase):
editor: The ExternalEditor instance to test.
"""
def setUp(self):
@pytest.fixture(autouse=True)
def setup(self, mocker, stubs):
mocker.patch('qutebrowser.misc.editor.message',
new=stubs.MessageModule())
mocker.patch('qutebrowser.misc.editor.QProcess',
new_callable=stubs.FakeQProcess)
mocker.patch('qutebrowser.misc.editor.config',
new=stubs.ConfigStub(
{'general': {'editor': [''],
'editor-encoding': 'utf-8'}}))
self.editor = editor.ExternalEditor(0)
def test_file_handling_closed_ok(self, _proc_mock):
def test_file_handling_closed_ok(self):
"""Test file handling when closing with an exit status == 0."""
self.editor.edit("")
filename = self.editor._filename
self.assertTrue(os.path.exists(filename))
assert os.path.exists(filename)
self.editor.on_proc_closed(0, QProcess.NormalExit)
self.assertFalse(os.path.exists(filename))
assert not os.path.exists(filename)
def test_file_handling_closed_error(self, _proc_mock):
def test_file_handling_closed_error(self, caplog):
"""Test file handling when closing with an exit status != 0."""
self.editor.edit("")
filename = self.editor._filename
self.assertTrue(os.path.exists(filename))
with self.assertLogs('message', logging.ERROR):
assert os.path.exists(filename)
with caplog.atLevel(logging.ERROR):
self.editor.on_proc_closed(1, QProcess.NormalExit)
self.assertFalse(os.path.exists(filename))
assert len(caplog.records()) == 2
assert not os.path.exists(filename)
def test_file_handling_closed_crash(self, _proc_mock):
def test_file_handling_closed_crash(self, caplog):
"""Test file handling when closing with a crash."""
self.editor.edit("")
filename = self.editor._filename
self.assertTrue(os.path.exists(filename))
with self.assertLogs('message', logging.ERROR):
assert os.path.exists(filename)
with caplog.atLevel(logging.ERROR):
self.editor.on_proc_error(QProcess.Crashed)
assert len(caplog.records()) == 2
self.editor.on_proc_closed(0, QProcess.CrashExit)
self.assertFalse(os.path.exists(filename))
assert not os.path.exists(filename)
@mock.patch('qutebrowser.misc.editor.QProcess',
new_callable=stubs.FakeQProcess)
@mock.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub(
{'general': {'editor': [''], 'editor-encoding': 'utf-8'}}))
class TextModifyTests(unittest.TestCase):
class TestModifyTests:
"""Tests to test if the text gets saved/loaded correctly.
@ -139,7 +139,12 @@ class TextModifyTests(unittest.TestCase):
editor: The ExternalEditor instance to test.
"""
def setUp(self):
@pytest.fixture(autouse=True)
def setup(self, mocker, stubs):
mocker.patch('qutebrowser.misc.editor.QProcess',
new_callable=stubs.FakeQProcess)
mocker.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub(
{'general': {'editor': [''], 'editor-encoding': 'utf-8'}}))
self.editor = editor.ExternalEditor(0)
self.editor.editing_finished = mock.Mock()
@ -164,45 +169,40 @@ class TextModifyTests(unittest.TestCase):
data = f.read()
return data
def test_empty_input(self, _proc_mock):
def test_empty_input(self):
"""Test if an empty input gets modified correctly."""
self.editor.edit("")
self.assertEqual(self._read(), "")
assert self._read() == ""
self._write("Hello")
self.editor.on_proc_closed(0, QProcess.NormalExit)
self.editor.editing_finished.emit.assert_called_with("Hello")
def test_simple_input(self, _proc_mock):
def test_simple_input(self):
"""Test if an empty input gets modified correctly."""
self.editor.edit("Hello")
self.assertEqual(self._read(), "Hello")
assert self._read() == "Hello"
self._write("World")
self.editor.on_proc_closed(0, QProcess.NormalExit)
self.editor.editing_finished.emit.assert_called_with("World")
def test_umlaut(self, _proc_mock):
def test_umlaut(self):
"""Test if umlauts works correctly."""
self.editor.edit("Hällö Wörld")
self.assertEqual(self._read(), "Hällö Wörld")
assert self._read() == "Hällö Wörld"
self._write("Überprüfung")
self.editor.on_proc_closed(0, QProcess.NormalExit)
self.editor.editing_finished.emit.assert_called_with("Überprüfung")
def test_unicode(self, _proc_mock):
def test_unicode(self):
"""Test if other UTF8 chars work correctly."""
self.editor.edit("\u2603") # Unicode snowman
self.assertEqual(self._read(), "\u2603")
assert self._read() == "\u2603"
self._write("\u2601") # Cloud
self.editor.on_proc_closed(0, QProcess.NormalExit)
self.editor.editing_finished.emit.assert_called_with("\u2601")
@mock.patch('qutebrowser.misc.editor.QProcess',
new_callable=stubs.FakeQProcess)
@mock.patch('qutebrowser.misc.editor.message', new=helpers.MessageModule())
@mock.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub(
{'general': {'editor': [''], 'editor-encoding': 'utf-8'}}))
class ErrorMessageTests(unittest.TestCase):
class TestErrorMessage:
"""Test if statusbar error messages get emitted correctly.
@ -210,21 +210,28 @@ class ErrorMessageTests(unittest.TestCase):
editor: The ExternalEditor instance to test.
"""
def setUp(self):
@pytest.yield_fixture(autouse=True)
def setup(self, mocker, stubs):
mocker.patch('qutebrowser.misc.editor.QProcess',
new_callable=stubs.FakeQProcess)
mocker.patch('qutebrowser.misc.editor.message',
new=stubs.MessageModule())
mocker.patch('qutebrowser.misc.editor.config', new=stubs.ConfigStub(
{'general': {'editor': [''], 'editor-encoding': 'utf-8'}}))
self.editor = editor.ExternalEditor(0)
yield
self.editor._cleanup() # pylint: disable=protected-access
def test_proc_error(self, _proc_mock):
def test_proc_error(self, caplog):
"""Test on_proc_error."""
self.editor.edit("")
with self.assertLogs('message', logging.ERROR):
with caplog.atLevel(logging.ERROR, 'message'):
self.editor.on_proc_error(QProcess.Crashed)
assert len(caplog.records()) == 2
def test_proc_return(self, _proc_mock):
def test_proc_return(self, caplog):
"""Test on_proc_finished with a bad exit status."""
self.editor.edit("")
with self.assertLogs('message', logging.ERROR):
with caplog.atLevel(logging.ERROR, 'message'):
self.editor.on_proc_closed(1, QProcess.NormalExit)
if __name__ == '__main__':
unittest.main()
assert len(caplog.records()) == 3

View File

@ -22,40 +22,40 @@
# pylint: disable=protected-access
import inspect
import unittest
from unittest import mock
from PyQt5.QtWidgets import QLineEdit
import pytest
from qutebrowser.misc import readline
from qutebrowser.test import stubs
@mock.patch('qutebrowser.misc.readline.QApplication',
new_callable=stubs.FakeQApplication)
class NoneWidgetTests(unittest.TestCase):
@pytest.fixture
def mocked_qapp(mocker, stubs):
"""Fixture that mocks readline.QApplication and returns it."""
return mocker.patch('qutebrowser.misc.readline.QApplication',
new_callable=stubs.FakeQApplication)
"""Tests when the focused widget is None."""
def setUp(self):
class TestNoneWidget:
"""Test if there are no exceptions when the widget is None."""
def test_none(self, mocked_qapp):
"""Call each rl_* method with a None focusWidget."""
self.bridge = readline.ReadlineBridge()
def test_none(self, qapp):
"""Test if there are no exceptions when the widget is None."""
qapp.focusWidget = mock.Mock(return_value=None)
mocked_qapp.focusWidget = mock.Mock(return_value=None)
for name, method in inspect.getmembers(self.bridge, inspect.ismethod):
with self.subTest(name=name):
if name.startswith('rl_'):
method()
if name.startswith('rl_'):
method()
@mock.patch('qutebrowser.misc.readline.QApplication',
new_callable=stubs.FakeQApplication)
class ReadlineBridgeTest(unittest.TestCase):
class TestReadlineBridgeTest:
"""Tests for readline bridge."""
def setUp(self):
@pytest.fixture(autouse=True)
def setup(self):
self.qle = mock.Mock()
self.qle.__class__ = QLineEdit
self.bridge = readline.ReadlineBridge()
@ -64,104 +64,100 @@ class ReadlineBridgeTest(unittest.TestCase):
"""Set the value the fake QLineEdit should return for selectedText."""
self.qle.configure_mock(**{'selectedText.return_value': text})
def test_rl_backward_char(self, qapp):
def test_rl_backward_char(self, mocked_qapp):
"""Test rl_backward_char."""
qapp.focusWidget = mock.Mock(return_value=self.qle)
mocked_qapp.focusWidget = mock.Mock(return_value=self.qle)
self.bridge.rl_backward_char()
self.qle.cursorBackward.assert_called_with(False)
def test_rl_forward_char(self, qapp):
def test_rl_forward_char(self, mocked_qapp):
"""Test rl_forward_char."""
qapp.focusWidget = mock.Mock(return_value=self.qle)
mocked_qapp.focusWidget = mock.Mock(return_value=self.qle)
self.bridge.rl_forward_char()
self.qle.cursorForward.assert_called_with(False)
def test_rl_backward_word(self, qapp):
def test_rl_backward_word(self, mocked_qapp):
"""Test rl_backward_word."""
qapp.focusWidget = mock.Mock(return_value=self.qle)
mocked_qapp.focusWidget = mock.Mock(return_value=self.qle)
self.bridge.rl_backward_word()
self.qle.cursorWordBackward.assert_called_with(False)
def test_rl_forward_word(self, qapp):
def test_rl_forward_word(self, mocked_qapp):
"""Test rl_forward_word."""
qapp.focusWidget = mock.Mock(return_value=self.qle)
mocked_qapp.focusWidget = mock.Mock(return_value=self.qle)
self.bridge.rl_forward_word()
self.qle.cursorWordForward.assert_called_with(False)
def test_rl_beginning_of_line(self, qapp):
def test_rl_beginning_of_line(self, mocked_qapp):
"""Test rl_beginning_of_line."""
qapp.focusWidget = mock.Mock(return_value=self.qle)
mocked_qapp.focusWidget = mock.Mock(return_value=self.qle)
self.bridge.rl_beginning_of_line()
self.qle.home.assert_called_with(False)
def test_rl_end_of_line(self, qapp):
def test_rl_end_of_line(self, mocked_qapp):
"""Test rl_end_of_line."""
qapp.focusWidget = mock.Mock(return_value=self.qle)
mocked_qapp.focusWidget = mock.Mock(return_value=self.qle)
self.bridge.rl_end_of_line()
self.qle.end.assert_called_with(False)
def test_rl_delete_char(self, qapp):
def test_rl_delete_char(self, mocked_qapp):
"""Test rl_delete_char."""
qapp.focusWidget = mock.Mock(return_value=self.qle)
mocked_qapp.focusWidget = mock.Mock(return_value=self.qle)
self.bridge.rl_delete_char()
self.qle.del_.assert_called_with()
def test_rl_backward_delete_char(self, qapp):
def test_rl_backward_delete_char(self, mocked_qapp):
"""Test rl_backward_delete_char."""
qapp.focusWidget = mock.Mock(return_value=self.qle)
mocked_qapp.focusWidget = mock.Mock(return_value=self.qle)
self.bridge.rl_backward_delete_char()
self.qle.backspace.assert_called_with()
def test_rl_unix_line_discard(self, qapp):
def test_rl_unix_line_discard(self, mocked_qapp):
"""Set a selected text, delete it, see if it comes back with yank."""
qapp.focusWidget = mock.Mock(return_value=self.qle)
mocked_qapp.focusWidget = mock.Mock(return_value=self.qle)
self._set_selected_text("delete test")
self.bridge.rl_unix_line_discard()
self.qle.home.assert_called_with(True)
self.assertEqual(self.bridge._deleted[self.qle], "delete test")
assert self.bridge._deleted[self.qle] == "delete test"
self.qle.del_.assert_called_with()
self.bridge.rl_yank()
self.qle.insert.assert_called_with("delete test")
def test_rl_kill_line(self, qapp):
def test_rl_kill_line(self, mocked_qapp):
"""Set a selected text, delete it, see if it comes back with yank."""
qapp.focusWidget = mock.Mock(return_value=self.qle)
mocked_qapp.focusWidget = mock.Mock(return_value=self.qle)
self._set_selected_text("delete test")
self.bridge.rl_kill_line()
self.qle.end.assert_called_with(True)
self.assertEqual(self.bridge._deleted[self.qle], "delete test")
assert self.bridge._deleted[self.qle] == "delete test"
self.qle.del_.assert_called_with()
self.bridge.rl_yank()
self.qle.insert.assert_called_with("delete test")
def test_rl_unix_word_rubout(self, qapp):
def test_rl_unix_word_rubout(self, mocked_qapp):
"""Set a selected text, delete it, see if it comes back with yank."""
qapp.focusWidget = mock.Mock(return_value=self.qle)
mocked_qapp.focusWidget = mock.Mock(return_value=self.qle)
self._set_selected_text("delete test")
self.bridge.rl_unix_word_rubout()
self.qle.cursorWordBackward.assert_called_with(True)
self.assertEqual(self.bridge._deleted[self.qle], "delete test")
assert self.bridge._deleted[self.qle] == "delete test"
self.qle.del_.assert_called_with()
self.bridge.rl_yank()
self.qle.insert.assert_called_with("delete test")
def test_rl_kill_word(self, qapp):
def test_rl_kill_word(self, mocked_qapp):
"""Set a selected text, delete it, see if it comes back with yank."""
qapp.focusWidget = mock.Mock(return_value=self.qle)
mocked_qapp.focusWidget = mock.Mock(return_value=self.qle)
self._set_selected_text("delete test")
self.bridge.rl_kill_word()
self.qle.cursorWordForward.assert_called_with(True)
self.assertEqual(self.bridge._deleted[self.qle], "delete test")
assert self.bridge._deleted[self.qle] == "delete test"
self.qle.del_.assert_called_with()
self.bridge.rl_yank()
self.qle.insert.assert_called_with("delete test")
def test_rl_yank_no_text(self, qapp):
def test_rl_yank_no_text(self, mocked_qapp):
"""Test yank without having deleted anything."""
qapp.focusWidget = mock.Mock(return_value=self.qle)
mocked_qapp.focusWidget = mock.Mock(return_value=self.qle)
self.bridge.rl_yank()
self.assertFalse(self.qle.insert.called)
if __name__ == '__main__':
unittest.main()
assert not self.qle.insert.called

View File

@ -21,6 +21,8 @@
"""Fake objects/stubs."""
import logging
from unittest import mock
from PyQt5.QtCore import pyqtSignal, QPoint, QProcess, QObject
@ -37,8 +39,8 @@ class ConfigStub:
data: The config data to return.
"""
def __init__(self, data):
self.data = data
def __init__(self, data=None):
self.data = data or {}
def section(self, name):
"""Get a section from the config.
@ -257,7 +259,7 @@ class FakeTimer(QObject):
def setSingleShot(self, singleshot):
self._singleshot = singleshot
def singleShot(self):
def isSingleShot(self):
return self._singleshot
def start(self):
@ -268,3 +270,20 @@ class FakeTimer(QObject):
def isActive(self):
return self._started
class MessageModule:
"""A drop-in replacement for qutebrowser.utils.message."""
def error(self, _win_id, message, _immediately=False):
"""Log an error to the message logger."""
logging.getLogger('message').error(message)
def warning(self, _win_id, message, _immediately=False):
"""Log a warning to the message logger."""
logging.getLogger('message').warning(message)
def info(self, _win_id, message, _immediately=True):
"""Log an info message to the message logger."""
logging.getLogger('message').info(message)

107
tests/test_stubs.py Normal file
View File

@ -0,0 +1,107 @@
# 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/>.
"""Test test stubs."""
from unittest import mock
import pytest
@pytest.fixture
def timer(stubs):
return stubs.FakeTimer()
def test_timeout(timer):
"""Test whether timeout calls the functions."""
func = mock.Mock()
func2 = mock.Mock()
timer.timeout.connect(func)
timer.timeout.connect(func2)
assert not func.called
assert not func2.called
timer.timeout.emit()
func.assert_called_once_with()
func2.assert_called_once_with()
def test_disconnect_all(timer):
"""Test disconnect without arguments."""
func = mock.Mock()
timer.timeout.connect(func)
timer.timeout.disconnect()
timer.timeout.emit()
assert not func.called
def test_disconnect_one(timer):
"""Test disconnect with a single argument."""
func = mock.Mock()
timer.timeout.connect(func)
timer.timeout.disconnect(func)
timer.timeout.emit()
assert not func.called
def test_disconnect_all_invalid(timer):
"""Test disconnecting with no connections."""
with pytest.raises(TypeError):
timer.timeout.disconnect()
def test_disconnect_one_invalid(timer):
"""Test disconnecting with an invalid connection."""
func1 = mock.Mock()
func2 = mock.Mock()
timer.timeout.connect(func1)
with pytest.raises(TypeError):
timer.timeout.disconnect(func2)
assert not func1.called
assert not func2.called
timer.timeout.emit()
func1.assert_called_once_with()
def test_singleshot(timer):
"""Test setting singleShot."""
assert not timer.isSingleShot()
timer.setSingleShot(True)
assert timer.isSingleShot()
timer.start()
assert timer.isActive()
timer.timeout.emit()
assert not timer.isActive()
def test_active(timer):
"""Test isActive."""
assert not timer.isActive()
timer.start()
assert timer.isActive()
timer.stop()
assert not timer.isActive()
def test_interval(timer):
"""Test setting an interval."""
assert timer.interval() == 0
timer.setInterval(1000)
assert timer.interval() == 1000

View File

@ -42,4 +42,4 @@ def test_log_time(caplog):
assert match
duration = float(match.group(1))
assert 0.09 <= duration <= 0.11
assert 0.08 <= duration <= 0.12

View File

@ -21,7 +21,6 @@
import pytest
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import QStyle, QFrame
from qutebrowser.utils import debug

View File

@ -21,12 +21,11 @@
import pytest
from qutebrowser.test import stubs
from qutebrowser.utils import debug
@pytest.fixture
def signal():
def signal(stubs):
"""Fixture to provide a faked pyqtSignal."""
return stubs.FakeSignal()

View File

@ -0,0 +1,116 @@
# 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/>.
"""Tests for qutebrowser.utils.standarddir."""
import os
import os.path
import sys
from PyQt5.QtWidgets import QApplication
import pytest
from qutebrowser.utils import standarddir
@pytest.yield_fixture(autouse=True)
def change_qapp_name():
"""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')
yield
QApplication.instance().setApplicationName(old_name)
@pytest.mark.skipif(not sys.platform.startswith("linux"),
reason="requires Linux")
class TestGetStandardDirLinux:
"""Tests for standarddir under Linux."""
def test_data_explicit(self, monkeypatch, tmpdir):
"""Test data dir with XDG_DATA_HOME explicitly set."""
monkeypatch.setenv('XDG_DATA_HOME', str(tmpdir))
standarddir.init(None)
assert standarddir.data() == str(tmpdir / 'qutebrowser_test')
def test_config_explicit(self, monkeypatch, tmpdir):
"""Test config dir with XDG_CONFIG_HOME explicitly set."""
monkeypatch.setenv('XDG_CONFIG_HOME', str(tmpdir))
standarddir.init(None)
assert standarddir.config() == str(tmpdir / 'qutebrowser_test')
def test_cache_explicit(self, monkeypatch, tmpdir):
"""Test cache dir with XDG_CACHE_HOME explicitly set."""
monkeypatch.setenv('XDG_CACHE_HOME', str(tmpdir))
standarddir.init(None)
assert standarddir.cache() == str(tmpdir / 'qutebrowser_test')
def test_data(self, monkeypatch, tmpdir):
"""Test data dir with XDG_DATA_HOME not set."""
monkeypatch.setenv('HOME', str(tmpdir))
monkeypatch.delenv('XDG_DATA_HOME', raising=False)
standarddir.init(None)
expected = tmpdir / '.local' / 'share' / 'qutebrowser_test'
assert standarddir.data() == str(expected)
def test_config(self, monkeypatch, tmpdir):
"""Test config dir with XDG_CONFIG_HOME not set."""
monkeypatch.setenv('HOME', str(tmpdir))
monkeypatch.delenv('XDG_CONFIG_HOME', raising=False)
standarddir.init(None)
expected = tmpdir / '.config' / 'qutebrowser_test'
assert standarddir.config() == str(expected)
def test_cache(self, monkeypatch, tmpdir):
"""Test cache dir with XDG_CACHE_HOME not set."""
monkeypatch.setenv('HOME', str(tmpdir))
monkeypatch.delenv('XDG_CACHE_HOME', raising=False)
standarddir.init(None)
expected = tmpdir / '.cache' / 'qutebrowser_test'
assert standarddir.cache() == expected
@pytest.mark.skipif(not sys.platform.startswith("win"),
reason="requires Windows")
class TestGetStandardDirWindows:
"""Tests for standarddir under Windows."""
@pytest.fixture(autouse=True)
def reset_standarddir(self):
standarddir.init(None)
def test_data(self):
"""Test data dir."""
expected = ['qutebrowser_test', 'data']
assert standarddir.data().split(os.sep)[-2:] == expected
def test_config(self):
"""Test config dir."""
assert standarddir.config().split(os.sep)[-1] == 'qutebrowser_test'
def test_cache(self):
"""Test cache dir."""
expected = ['qutebrowser_test', 'cache']
assert standarddir.cache().split(os.sep)[-2:] == expected

View File

@ -21,13 +21,10 @@
"""Tests for qutebrowser.utils.urlutils."""
import unittest
from unittest import mock
from PyQt5.QtCore import QUrl
import pytest
from qutebrowser.utils import urlutils
from qutebrowser.test import stubs
def get_config_stub(auto_search=True):
@ -45,7 +42,7 @@ def get_config_stub(auto_search=True):
}
class SpecialURLTests(unittest.TestCase):
class TestSpecialURL:
"""Test is_special_url.
@ -65,65 +62,67 @@ class SpecialURLTests(unittest.TestCase):
'www.qutebrowser.org'
)
def test_special_urls(self):
@pytest.mark.parametrize('url', SPECIAL_URLS)
def test_special_urls(self, url):
"""Test special URLs."""
for url in self.SPECIAL_URLS:
with self.subTest(url=url):
u = QUrl(url)
self.assertTrue(urlutils.is_special_url(u))
u = QUrl(url)
assert urlutils.is_special_url(u)
def test_normal_urls(self):
@pytest.mark.parametrize('url', NORMAL_URLS)
def test_normal_urls(self, url):
"""Test non-special URLs."""
for url in self.NORMAL_URLS:
with self.subTest(url=url):
u = QUrl(url)
self.assertFalse(urlutils.is_special_url(u))
u = QUrl(url)
assert not urlutils.is_special_url(u)
@mock.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub(
get_config_stub()))
class SearchUrlTests(unittest.TestCase):
class TestSearchUrl:
"""Test _get_search_url."""
@pytest.fixture(autouse=True)
def mock_config(self, stubs, mocker):
"""Fixture to patch urlutils.config with a stub."""
mocker.patch('qutebrowser.utils.urlutils.config',
new=stubs.ConfigStub(get_config_stub()))
def test_default_engine(self):
"""Test default search engine."""
url = urlutils._get_search_url('testfoo')
self.assertEqual(url.host(), 'www.example.com')
self.assertEqual(url.query(), 'q=testfoo')
assert url.host() == 'www.example.com'
assert url.query() == 'q=testfoo'
def test_engine_pre(self):
"""Test search engine name with one word."""
url = urlutils._get_search_url('test testfoo')
self.assertEqual(url.host(), 'www.qutebrowser.org')
self.assertEqual(url.query(), 'q=testfoo')
assert url.host() == 'www.qutebrowser.org'
assert url.query() == 'q=testfoo'
def test_engine_pre_multiple_words(self):
"""Test search engine name with multiple words."""
url = urlutils._get_search_url('test testfoo bar foo')
self.assertEqual(url.host(), 'www.qutebrowser.org')
self.assertEqual(url.query(), 'q=testfoo bar foo')
assert url.host() == 'www.qutebrowser.org'
assert url.query() == 'q=testfoo bar foo'
def test_engine_pre_whitespace_at_end(self):
"""Test search engine name with one word and whitespace."""
url = urlutils._get_search_url('test testfoo ')
self.assertEqual(url.host(), 'www.qutebrowser.org')
self.assertEqual(url.query(), 'q=testfoo')
assert url.host() == 'www.qutebrowser.org'
assert url.query() == 'q=testfoo'
def test_engine_with_bang_pre(self):
"""Test search engine with a prepended !bang."""
url = urlutils._get_search_url('!python testfoo')
self.assertEqual(url.host(), 'www.example.com')
self.assertEqual(url.query(), 'q=%21python testfoo')
assert url.host() == 'www.example.com'
assert url.query() == 'q=%21python testfoo'
def test_engine_wrong(self):
"""Test with wrong search engine."""
url = urlutils._get_search_url('blub testfoo')
self.assertEqual(url.host(), 'www.example.com')
self.assertEqual(url.query(), 'q=blub testfoo')
assert url.host() == 'www.example.com'
assert url.query() == 'q=blub testfoo'
class IsUrlTests(unittest.TestCase):
class TestIsUrl:
"""Tests for is_url.
@ -158,87 +157,72 @@ class IsUrlTests(unittest.TestCase):
'foo::bar',
)
@mock.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub(
get_config_stub('naive')))
def test_urls(self):
@pytest.mark.parametrize('url', URLS)
def test_urls(self, mocker, stubs, url):
"""Test things which are URLs."""
for url in self.URLS:
with self.subTest(url=url):
self.assertTrue(urlutils.is_url(url), url)
mocker.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub(
get_config_stub('naive')))
assert urlutils.is_url(url), url
@mock.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub(
get_config_stub('naive')))
def test_not_urls(self):
@pytest.mark.parametrize('url', NOT_URLS)
def test_not_urls(self, mocker, stubs, url):
"""Test things which are not URLs."""
for url in self.NOT_URLS:
with self.subTest(url=url):
self.assertFalse(urlutils.is_url(url), url)
mocker.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub(
get_config_stub('naive')))
assert not urlutils.is_url(url), url
@mock.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub(
get_config_stub(True)))
def test_search_autosearch(self):
@pytest.mark.parametrize('autosearch', [True, False])
def test_search_autosearch(self, mocker, stubs, autosearch):
"""Test explicit search with auto-search=True."""
self.assertFalse(urlutils.is_url('test foo'))
@mock.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub(
get_config_stub(False)))
def test_search_no_autosearch(self):
"""Test explicit search with auto-search=False."""
self.assertFalse(urlutils.is_url('test foo'))
mocker.patch('qutebrowser.utils.urlutils.config', new=stubs.ConfigStub(
get_config_stub(autosearch)))
assert not urlutils.is_url('test foo')
class QurlFromUserInputTests(unittest.TestCase):
class TestQurlFromUserInput:
"""Tests for qurl_from_user_input."""
def test_url(self):
"""Test a normal URL."""
self.assertEqual(
urlutils.qurl_from_user_input('qutebrowser.org').toString(),
'http://qutebrowser.org')
url = urlutils.qurl_from_user_input('qutebrowser.org')
assert url.toString() == 'http://qutebrowser.org'
def test_url_http(self):
"""Test a normal URL with http://."""
self.assertEqual(
urlutils.qurl_from_user_input('http://qutebrowser.org').toString(),
'http://qutebrowser.org')
url = urlutils.qurl_from_user_input('http://qutebrowser.org')
assert url.toString() == 'http://qutebrowser.org'
def test_ipv6_bare(self):
"""Test an IPv6 without brackets."""
self.assertEqual(urlutils.qurl_from_user_input('::1/foo').toString(),
'http://[::1]/foo')
url = urlutils.qurl_from_user_input('::1/foo')
assert url.toString() == 'http://[::1]/foo'
def test_ipv6(self):
"""Test an IPv6 with brackets."""
self.assertEqual(urlutils.qurl_from_user_input('[::1]/foo').toString(),
'http://[::1]/foo')
url = urlutils.qurl_from_user_input('[::1]/foo')
assert url.toString() == 'http://[::1]/foo'
def test_ipv6_http(self):
"""Test an IPv6 with http:// and brackets."""
self.assertEqual(
urlutils.qurl_from_user_input('http://[::1]').toString(),
'http://[::1]')
url = urlutils.qurl_from_user_input('http://[::1]')
assert url.toString() == 'http://[::1]'
class FilenameFromUrlTests(unittest.TestCase):
class TestFilenameFromUrl:
"""Tests for filename_from_url."""
def test_invalid_url(self):
"""Test with an invalid QUrl."""
self.assertEqual(urlutils.filename_from_url(QUrl()), None)
assert urlutils.filename_from_url(QUrl()) is None
def test_url_path(self):
"""Test with an URL with path."""
url = QUrl('http://qutebrowser.org/test.html')
self.assertEqual(urlutils.filename_from_url(url), 'test.html')
assert urlutils.filename_from_url(url) == 'test.html'
def test_url_host(self):
"""Test with an URL with no path."""
url = QUrl('http://qutebrowser.org/')
self.assertEqual(urlutils.filename_from_url(url),
'qutebrowser.org.html')
if __name__ == '__main__':
unittest.main()
assert urlutils.filename_from_url(url) == 'qutebrowser.org.html'

View File

@ -21,15 +21,14 @@
import sys
import enum
import unittest
import datetime
import os.path
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor
import pytest
from qutebrowser.utils import utils, qtutils
from qutebrowser.test import helpers
class Color(QColor):
@ -42,7 +41,7 @@ class Color(QColor):
alpha=self.alpha())
class ElidingTests(unittest.TestCase):
class TestEliding:
"""Test elide."""
@ -50,33 +49,33 @@ class ElidingTests(unittest.TestCase):
def test_too_small(self):
"""Test eliding to 0 chars which should fail."""
with self.assertRaises(ValueError):
with pytest.raises(ValueError):
utils.elide('foo', 0)
def test_length_one(self):
"""Test eliding to 1 char which should yield ..."""
self.assertEqual(utils.elide('foo', 1), self.ELLIPSIS)
assert utils.elide('foo', 1) == self.ELLIPSIS
def test_fits(self):
"""Test eliding with a string which fits exactly."""
self.assertEqual(utils.elide('foo', 3), 'foo')
assert utils.elide('foo', 3) == 'foo'
def test_elided(self):
"""Test eliding with a string which should get elided."""
self.assertEqual(utils.elide('foobar', 3), 'fo' + self.ELLIPSIS)
assert utils.elide('foobar', 3) == 'fo' + self.ELLIPSIS
class ReadFileTests(unittest.TestCase):
class TestReadFile:
"""Test read_file."""
def test_readfile(self):
"""Read a test file."""
content = utils.read_file(os.path.join('test', 'testfile'))
self.assertEqual(content.splitlines()[0], "Hello World!")
content = utils.read_file(os.path.join('utils', 'testfile'))
assert content.splitlines()[0] == "Hello World!"
class InterpolateColorTests(unittest.TestCase):
class TestInterpolateColor:
"""Tests for interpolate_color.
@ -85,30 +84,31 @@ class InterpolateColorTests(unittest.TestCase):
white: The Color black as a valid Color for tests.
"""
def setUp(self):
@pytest.fixture(autouse=True)
def setup(self):
self.white = Color('white')
self.black = Color('black')
def test_invalid_start(self):
"""Test an invalid start color."""
with self.assertRaises(qtutils.QtValueError):
with pytest.raises(qtutils.QtValueError):
utils.interpolate_color(Color(), self.white, 0)
def test_invalid_end(self):
"""Test an invalid end color."""
with self.assertRaises(qtutils.QtValueError):
with pytest.raises(qtutils.QtValueError):
utils.interpolate_color(self.white, Color(), 0)
def test_invalid_percentage(self):
"""Test an invalid percentage."""
with self.assertRaises(ValueError):
with pytest.raises(ValueError):
utils.interpolate_color(self.white, self.white, -1)
with self.assertRaises(ValueError):
with pytest.raises(ValueError):
utils.interpolate_color(self.white, self.white, 101)
def test_invalid_colorspace(self):
"""Test an invalid colorspace."""
with self.assertRaises(ValueError):
with pytest.raises(ValueError):
utils.interpolate_color(self.white, self.black, 10, QColor.Cmyk)
def test_valid_percentages_rgb(self):
@ -116,30 +116,30 @@ class InterpolateColorTests(unittest.TestCase):
white = utils.interpolate_color(self.white, self.black, 0, QColor.Rgb)
black = utils.interpolate_color(self.white, self.black, 100,
QColor.Rgb)
self.assertEqual(Color(white), self.white)
self.assertEqual(Color(black), self.black)
assert Color(white) == self.white
assert Color(black) == self.black
def test_valid_percentages_hsv(self):
"""Test 0% and 100% in the HSV colorspace."""
white = utils.interpolate_color(self.white, self.black, 0, QColor.Hsv)
black = utils.interpolate_color(self.white, self.black, 100,
QColor.Hsv)
self.assertEqual(Color(white), self.white)
self.assertEqual(Color(black), self.black)
assert Color(white) == self.white
assert Color(black) == self.black
def test_valid_percentages_hsl(self):
"""Test 0% and 100% in the HSL colorspace."""
white = utils.interpolate_color(self.white, self.black, 0, QColor.Hsl)
black = utils.interpolate_color(self.white, self.black, 100,
QColor.Hsl)
self.assertEqual(Color(white), self.white)
self.assertEqual(Color(black), self.black)
assert Color(white) == self.white
assert Color(black) == self.black
def test_interpolation_rgb(self):
"""Test an interpolation in the RGB colorspace."""
color = utils.interpolate_color(Color(0, 40, 100), Color(0, 20, 200),
50, QColor.Rgb)
self.assertEqual(Color(color), Color(0, 30, 150))
assert Color(color) == Color(0, 30, 150)
def test_interpolation_hsv(self):
"""Test an interpolation in the HSV colorspace."""
@ -150,7 +150,7 @@ class InterpolateColorTests(unittest.TestCase):
color = utils.interpolate_color(start, stop, 50, QColor.Hsv)
expected = Color()
expected.setHsv(0, 30, 150)
self.assertEqual(Color(color), expected)
assert Color(color) == expected
def test_interpolation_hsl(self):
"""Test an interpolation in the HSL colorspace."""
@ -161,10 +161,10 @@ class InterpolateColorTests(unittest.TestCase):
color = utils.interpolate_color(start, stop, 50, QColor.Hsl)
expected = Color()
expected.setHsl(0, 30, 150)
self.assertEqual(Color(color), expected)
assert Color(color) == expected
class FormatSecondsTests(unittest.TestCase):
class TestFormatSeconds:
"""Tests for format_seconds.
@ -186,14 +186,13 @@ class FormatSecondsTests(unittest.TestCase):
(36000, '10:00:00'),
]
def test_format_seconds(self):
@pytest.mark.parametrize('seconds, out', TESTS)
def test_format_seconds(self, seconds, out):
"""Test format_seconds with several tests."""
for seconds, out in self.TESTS:
with self.subTest(seconds=seconds):
self.assertEqual(utils.format_seconds(seconds), out)
assert utils.format_seconds(seconds) == out
class FormatTimedeltaTests(unittest.TestCase):
class TestFormatTimedelta:
"""Tests for format_timedelta.
@ -217,14 +216,13 @@ class FormatTimedeltaTests(unittest.TestCase):
(datetime.timedelta(seconds=36000), '10h'),
]
def test_format_seconds(self):
@pytest.mark.parametrize('td, out', TESTS)
def test_format_seconds(self, td, out):
"""Test format_seconds with several tests."""
for td, out in self.TESTS:
with self.subTest(td=td):
self.assertEqual(utils.format_timedelta(td), out)
assert utils.format_timedelta(td) == out
class FormatSizeTests(unittest.TestCase):
class TestFormatSize:
"""Tests for format_size.
@ -244,122 +242,117 @@ class FormatSizeTests(unittest.TestCase):
(None, '?.??'),
]
def test_format_size(self):
KILO_TESTS = [(999, '999.00'), (1000, '1.00k'), (1010, '1.01k')]
@pytest.mark.parametrize('size, out', TESTS)
def test_format_size(self, size, out):
"""Test format_size with several tests."""
for size, out in self.TESTS:
with self.subTest(size=size):
self.assertEqual(utils.format_size(size), out)
assert utils.format_size(size) == out
def test_suffix(self):
@pytest.mark.parametrize('size, out', TESTS)
def test_suffix(self, size, out):
"""Test the suffix option."""
for size, out in self.TESTS:
with self.subTest(size=size):
self.assertEqual(utils.format_size(size, suffix='B'),
out + 'B')
assert utils.format_size(size, suffix='B') == out + 'B'
def test_base(self):
@pytest.mark.parametrize('size, out', KILO_TESTS)
def test_base(self, size, out):
"""Test with an alternative base."""
kilo_tests = [(999, '999.00'), (1000, '1.00k'), (1010, '1.01k')]
for size, out in kilo_tests:
with self.subTest(size=size):
self.assertEqual(utils.format_size(size, base=1000), out)
assert utils.format_size(size, base=1000) == out
class KeyToStringTests(unittest.TestCase):
class TestKeyToString:
"""Test key_to_string."""
def test_unicode_garbage_keys(self):
"""Test a special key where QKeyEvent::toString works incorrectly."""
self.assertEqual(utils.key_to_string(Qt.Key_Blue), 'Blue')
assert utils.key_to_string(Qt.Key_Blue) == 'Blue'
def test_backtab(self):
"""Test if backtab is normalized to tab correctly."""
self.assertEqual(utils.key_to_string(Qt.Key_Backtab), 'Tab')
assert utils.key_to_string(Qt.Key_Backtab) == 'Tab'
def test_escape(self):
"""Test if escape is normalized to escape correctly."""
self.assertEqual(utils.key_to_string(Qt.Key_Escape), 'Escape')
assert utils.key_to_string(Qt.Key_Escape) == 'Escape'
def test_letter(self):
"""Test a simple letter key."""
self.assertEqual(utils.key_to_string(Qt.Key_A), 'A')
assert utils.key_to_string(Qt.Key_A) == 'A'
def test_unicode(self):
"""Test a printable unicode key."""
self.assertEqual(utils.key_to_string(Qt.Key_degree), '°')
assert utils.key_to_string(Qt.Key_degree) == '°'
def test_special(self):
"""Test a non-printable key handled by QKeyEvent::toString."""
self.assertEqual(utils.key_to_string(Qt.Key_F1), 'F1')
assert utils.key_to_string(Qt.Key_F1) == 'F1'
class KeyEventToStringTests(unittest.TestCase):
class TestKeyEventToString:
"""Test keyevent_to_string."""
def test_only_control(self):
"""Test keyevent when only control is pressed."""
evt = helpers.fake_keyevent(key=Qt.Key_Control,
def test_only_control(self, fake_keyevent_factory):
"""Test keyeevent when only control is pressed."""
evt = fake_keyevent_factory(key=Qt.Key_Control,
modifiers=Qt.ControlModifier)
self.assertIsNone(utils.keyevent_to_string(evt))
assert utils.keyevent_to_string(evt) is None
def test_only_hyper_l(self):
"""Test keyevent when only Hyper_L is pressed."""
evt = helpers.fake_keyevent(key=Qt.Key_Hyper_L,
def test_only_hyper_l(self, fake_keyevent_factory):
"""Test keyeevent when only Hyper_L is pressed."""
evt = fake_keyevent_factory(key=Qt.Key_Hyper_L,
modifiers=Qt.MetaModifier)
self.assertIsNone(utils.keyevent_to_string(evt))
assert utils.keyevent_to_string(evt) is None
def test_only_key(self):
def test_only_key(self, fake_keyevent_factory):
"""Test with a simple key pressed."""
evt = helpers.fake_keyevent(key=Qt.Key_A)
self.assertEqual(utils.keyevent_to_string(evt), 'A')
evt = fake_keyevent_factory(key=Qt.Key_A)
assert utils.keyevent_to_string(evt) == 'A'
def test_key_and_modifier(self):
def test_key_and_modifier(self, fake_keyevent_factory):
"""Test with key and modifier pressed."""
evt = helpers.fake_keyevent(key=Qt.Key_A, modifiers=Qt.ControlModifier)
self.assertEqual(utils.keyevent_to_string(evt), 'Ctrl+A')
evt = fake_keyevent_factory(key=Qt.Key_A, modifiers=Qt.ControlModifier)
assert utils.keyevent_to_string(evt) == 'Ctrl+A'
def test_key_and_modifiers(self):
def test_key_and_modifiers(self, fake_keyevent_factory):
"""Test with key and multiple modifier pressed."""
evt = helpers.fake_keyevent(
evt = fake_keyevent_factory(
key=Qt.Key_A, modifiers=(Qt.ControlModifier | Qt.AltModifier |
Qt.MetaModifier | Qt.ShiftModifier))
if sys.platform == 'darwin':
self.assertEqual(utils.keyevent_to_string(evt),
'Ctrl+Alt+Shift+A')
assert utils.keyevent_to_string(evt) == 'Ctrl+Alt+Shift+A'
else:
self.assertEqual(utils.keyevent_to_string(evt),
'Ctrl+Alt+Meta+Shift+A')
assert utils.keyevent_to_string(evt) == 'Ctrl+Alt+Meta+Shift+A'
class NormalizeTests(unittest.TestCase):
class TestNormalize:
"""Test normalize_keystr."""
def test_normalize(self):
STRINGS = (
('Control+x', 'ctrl+x'),
('Windows+x', 'meta+x'),
('Mod1+x', 'alt+x'),
('Mod4+x', 'meta+x'),
('Control--', 'ctrl+-'),
('Windows++', 'meta++'),
)
@pytest.mark.parametrize('orig, repl', STRINGS)
def test_normalize(self, orig, repl):
"""Test normalize with some strings."""
strings = (
('Control+x', 'ctrl+x'),
('Windows+x', 'meta+x'),
('Mod1+x', 'alt+x'),
('Mod4+x', 'meta+x'),
('Control--', 'ctrl+-'),
('Windows++', 'meta++'),
)
for orig, repl in strings:
with self.subTest(orig=orig):
self.assertEqual(utils.normalize_keystr(orig), repl)
assert utils.normalize_keystr(orig) == repl
class IsEnumTests(unittest.TestCase):
class TestIsEnum:
"""Test is_enum."""
def test_enum(self):
"""Test is_enum with an enum."""
e = enum.Enum('Foo', 'bar, baz')
self.assertTrue(utils.is_enum(e))
assert utils.is_enum(e)
def test_class(self):
"""Test is_enum with a non-enum class."""
@ -368,14 +361,15 @@ class IsEnumTests(unittest.TestCase):
"""Test class for is_enum."""
pass
self.assertFalse(utils.is_enum(Test))
assert not utils.is_enum(Test)
def test_object(self):
"""Test is_enum with a non-enum object."""
self.assertFalse(utils.is_enum(23))
assert not utils.is_enum(23)
class RaisesTests(unittest.TestCase):
class TestRaises:
"""Test raises."""
@ -389,106 +383,98 @@ class RaisesTests(unittest.TestCase):
def test_raises_single_exc_true(self):
"""Test raises with a single exception which gets raised."""
self.assertTrue(utils.raises(ValueError, int, 'a'))
assert utils.raises(ValueError, int, 'a')
def test_raises_single_exc_false(self):
"""Test raises with a single exception which does not get raised."""
self.assertFalse(utils.raises(ValueError, int, '1'))
assert not utils.raises(ValueError, int, '1')
def test_raises_multiple_exc_true(self):
"""Test raises with multiple exceptions which get raised."""
self.assertTrue(utils.raises((ValueError, TypeError), int, 'a'))
self.assertTrue(utils.raises((ValueError, TypeError), int, None))
assert utils.raises((ValueError, TypeError), int, 'a')
assert utils.raises((ValueError, TypeError), int, None)
def test_raises_multiple_exc_false(self):
"""Test raises with multiple exceptions which do not get raised."""
self.assertFalse(utils.raises((ValueError, TypeError), int, '1'))
assert not utils.raises((ValueError, TypeError), int, '1')
def test_no_args_true(self):
"""Test with no args and an exception which gets raised."""
self.assertTrue(utils.raises(Exception, self.do_raise))
assert utils.raises(Exception, self.do_raise)
def test_no_args_false(self):
"""Test with no args and an exception which does not get raised."""
self.assertFalse(utils.raises(Exception, self.do_nothing))
assert not utils.raises(Exception, self.do_nothing)
def test_unrelated_exception(self):
"""Test with an unrelated exception."""
with self.assertRaises(Exception):
with pytest.raises(Exception):
utils.raises(ValueError, self.do_raise)
class ForceEncodingTests(unittest.TestCase):
class TestForceEncoding:
"""Test force_encoding."""
def test_fitting_ascii(self):
"""Test with a text fitting into ASCII."""
text = 'hello world'
self.assertEqual(utils.force_encoding(text, 'ascii'), text)
TESTS = [
('hello world', 'ascii', 'hello world'),
('hellö wörld', 'utf-8', 'hellö wörld'),
('hellö wörld', 'ascii', 'hell? w?rld'),
]
def test_fitting_utf8(self):
"""Test with a text fitting into utf-8."""
text = 'hellö wörld'
self.assertEqual(utils.force_encoding(text, 'utf-8'), text)
def test_not_fitting_ascii(self):
"""Test with a text not fitting into ASCII."""
text = 'hellö wörld'
self.assertEqual(utils.force_encoding(text, 'ascii'), 'hell? w?rld')
@pytest.mark.parametrize('inp, enc, expected', TESTS)
def test_fitting_ascii(self, inp, enc, expected):
"""Test force_encoding will yield expected text."""
assert utils.force_encoding(inp, enc) == expected
class NewestSliceTests(unittest.TestCase):
class TestNewestSlice:
"""Test newest_slice."""
def test_count_minus_two(self):
"""Test with a count of -2."""
with self.assertRaises(ValueError):
with pytest.raises(ValueError):
utils.newest_slice([], -2)
def test_count_minus_one(self):
"""Test with a count of -1 (all elements)."""
items = range(20)
sliced = utils.newest_slice(items, -1)
self.assertEqual(list(sliced), list(items))
assert list(sliced) == list(items)
def test_count_zero(self):
"""Test with a count of 0 (no elements)."""
items = range(20)
sliced = utils.newest_slice(items, 0)
self.assertEqual(list(sliced), [])
assert list(sliced) == []
def test_count_much_smaller(self):
"""Test with a count which is much smaller than the iterable."""
items = range(20)
sliced = utils.newest_slice(items, 5)
self.assertEqual(list(sliced), [15, 16, 17, 18, 19])
assert list(sliced) == [15, 16, 17, 18, 19]
def test_count_smaller(self):
"""Test with a count which is exactly one smaller."""
items = range(5)
sliced = utils.newest_slice(items, 4)
self.assertEqual(list(sliced), [1, 2, 3, 4])
assert list(sliced) == [1, 2, 3, 4]
def test_count_equal(self):
"""Test with a count which is just as large as the iterable."""
items = range(5)
sliced = utils.newest_slice(items, 5)
self.assertEqual(list(sliced), list(items))
assert list(sliced) == list(items)
def test_count_bigger(self):
"""Test with a count which is one bigger than the iterable."""
items = range(5)
sliced = utils.newest_slice(items, 6)
self.assertEqual(list(sliced), list(items))
assert list(sliced) == list(items)
def test_count_much_bigger(self):
"""Test with a count which is much bigger than the iterable."""
items = range(5)
sliced = utils.newest_slice(items, 50)
self.assertEqual(list(sliced), list(items))
if __name__ == '__main__':
unittest.main()
assert list(sliced) == list(items)

14
tox.ini
View File

@ -20,6 +20,8 @@ deps =
py==1.4.26
pytest==2.7.0
pytest-capturelog==0.7
pytest-qt==1.3.0
pytest-mock==0.4.2
# We don't use {[testenv:mkvenv]commands} here because that seems to be broken
# on Ubuntu Trusty.
commands =
@ -39,8 +41,8 @@ commands =
[testenv:misc]
commands =
{envpython} scripts/misc_checks.py git
{envpython} scripts/misc_checks.py vcs qutebrowser scripts
{envpython} scripts/misc_checks.py spelling qutebrowser scripts
{envpython} scripts/misc_checks.py vcs qutebrowser scripts tests
{envpython} scripts/misc_checks.py spelling qutebrowser scripts tests
[testenv:pylint]
skip_install = true
@ -55,6 +57,7 @@ deps =
commands =
{[testenv:mkvenv]commands}
{envdir}/bin/pylint scripts qutebrowser --rcfile=.pylintrc --output-format=colorized --reports=no
{envpython} scripts/run_pylint_on_tests.py --rcfile=.pylintrc --output-format=colorized --reports=no
[testenv:pep257]
skip_install = true
@ -63,7 +66,7 @@ deps = pep257==0.5.0
# D102: Docstring missing, will be handled by others
# D209: Blank line before closing """ (removed from PEP257)
# D402: First line should not be function's signature (false-positives)
commands = {envpython} -m pep257 scripts qutebrowser --ignore=D102,D209,D402 '--match=(?!resources|test_content_disposition).*\.py'
commands = {envpython} -m pep257 scripts tests qutebrowser --ignore=D102,D103,D209,D402 '--match=(?!resources|test_content_disposition).*\.py'
[testenv:flake8]
skip_install = true
@ -74,7 +77,7 @@ deps =
flake8==2.4.0
commands =
{[testenv:mkvenv]commands}
{envdir}/bin/flake8 scripts qutebrowser --config=.flake8
{envdir}/bin/flake8 scripts tests qutebrowser --config=.flake8
[testenv:pyroma]
skip_install = true
@ -103,3 +106,6 @@ commands =
{envpython} scripts/src2asciidoc.py
git --no-pager diff --exit-code --stat
{envpython} scripts/asciidoc2html.py {posargs}
[pytest]
norecursedirs = .tox .venv