From 6d1b0ba2603a93d9e4c85ac28cb38c4b14d295d5 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 18 Aug 2015 20:43:42 +0200 Subject: [PATCH] Clean up conftest.py. --- tests/conftest.py | 270 ++++++++++++----------------------- tests/helpers/logfail.py | 31 ++++ tests/helpers/messagemock.py | 81 +++++++++++ 3 files changed, 202 insertions(+), 180 deletions(-) create mode 100644 tests/helpers/messagemock.py diff --git a/tests/conftest.py b/tests/conftest.py index 7b8ad5c0b..251ec60ef 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -17,6 +17,8 @@ # You should have received a copy of the GNU General Public License # along with qutebrowser. If not, see . +# pylint: disable=unused-import,import-error + """The qutebrowser test suite contest file.""" import os @@ -29,84 +31,10 @@ import pytest import helpers.stubs as stubsmod from helpers import logfail +from helpers.logfail import fail_on_logging, caplog_bug_workaround +from helpers.messagemock import message_mock from qutebrowser.config import config -from qutebrowser.utils import objreg, usertypes - - -try: - import pytest_capturelog as capturelog_mod -except ImportError: - # When using pytest for pyflakes/pep8/..., the plugin won't be available - # but conftest.py will still be loaded. - capturelog_mod = None - - -@pytest.yield_fixture(scope='session', autouse=True) -def fail_on_logging(): - handler = logfail.LogFailHandler() - logging.getLogger().addHandler(handler) - yield - logging.getLogger().removeHandler(handler) - handler.close() - - -@pytest.fixture(scope='session') -def stubs(): - """Provide access to stub objects useful for testing.""" - return stubsmod - - -@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(scope='session') -def qnam(qapp): - """Session-wide QNetworkAccessManager.""" - from PyQt5.QtNetwork import QNetworkAccessManager - nam = QNetworkAccessManager() - nam.setNetworkAccessible(QNetworkAccessManager.NotAccessible) - return nam - - -@pytest.fixture -def webpage(qnam): - """Get a new QWebPage object.""" - from PyQt5.QtWebKitWidgets import QWebPage - - page = QWebPage() - page.networkAccessManager().deleteLater() - page.setNetworkAccessManager(qnam) - return page - - -@pytest.fixture -def webframe(webpage): - """Convenience fixture to get a mainFrame of a QWebPage.""" - return webpage.mainFrame() - - -@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 +from qutebrowser.utils import objreg def pytest_collection_modifyitems(items): @@ -137,6 +65,47 @@ def pytest_collection_modifyitems(items): item.add_marker(skip_marker) +def pytest_runtest_setup(item): + """Add some custom markers.""" + if not isinstance(item, item.Function): + return + + if item.get_marker('posix') and os.name != 'posix': + pytest.skip("Requires a POSIX os.") + elif item.get_marker('windows') and os.name != 'nt': + pytest.skip("Requires Windows.") + elif item.get_marker('linux') and not sys.platform.startswith('linux'): + pytest.skip("Requires Linux.") + elif item.get_marker('osx') and sys.platform != 'darwin': + pytest.skip("Requires OS X.") + elif item.get_marker('not_frozen') and getattr(sys, 'frozen', False): + pytest.skip("Can't be run when frozen!") + elif item.get_marker('frozen') and not getattr(sys, 'frozen', False): + pytest.skip("Can only run when frozen!") + + +FakeWindow = collections.namedtuple('FakeWindow', ['registry']) + + +@pytest.yield_fixture +def win_registry(): + """Fixture providing a window registry for win_id 0.""" + registry = objreg.ObjectRegistry() + window = FakeWindow(registry) + objreg.window_registry[0] = window + yield registry + del objreg.window_registry[0] + + +@pytest.yield_fixture +def tab_registry(win_registry): + """Fixture providing a tab registry for win_id 0.""" + registry = objreg.ObjectRegistry() + objreg.register('tab-registry', registry, scope='window', window=0) + yield registry + objreg.delete('tab-registry', scope='window', window=0) + + def _generate_cmdline_tests(): """Generate testcases for test_split_binding.""" # pylint: disable=invalid-name @@ -211,119 +180,60 @@ def host_blocker_stub(stubs): objreg.delete('host-blocker') -def pytest_runtest_setup(item): - """Add some custom markers.""" - if not isinstance(item, item.Function): - return - - if item.get_marker('posix') and os.name != 'posix': - pytest.skip("Requires a POSIX os.") - elif item.get_marker('windows') and os.name != 'nt': - pytest.skip("Requires Windows.") - elif item.get_marker('linux') and not sys.platform.startswith('linux'): - pytest.skip("Requires Linux.") - elif item.get_marker('osx') and sys.platform != 'darwin': - pytest.skip("Requires OS X.") - elif item.get_marker('not_frozen') and getattr(sys, 'frozen', False): - pytest.skip("Can't be run when frozen!") - elif item.get_marker('frozen') and not getattr(sys, 'frozen', False): - pytest.skip("Can only run when frozen!") +@pytest.fixture(scope='session') +def stubs(): + """Provide access to stub objects useful for testing.""" + return stubsmod -class MessageMock: +@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 - """Helper object for message_mock. - Attributes: - _monkeypatch: The pytest monkeypatch fixture. - MessageLevel: An enum with possible message levels. - Message: A namedtuple representing a message. - messages: A list of Message tuples. - """ - - Message = collections.namedtuple('Message', ['level', 'win_id', 'text', - 'immediate']) - MessageLevel = usertypes.enum('Level', ('error', 'info', 'warning')) - - def __init__(self, monkeypatch): - self._monkeypatch = monkeypatch - self.messages = [] - - def _handle(self, level, win_id, text, immediately=False): - self.messages.append(self.Message(level, win_id, text, immediately)) - - def _handle_error(self, *args, **kwargs): - self._handle(self.MessageLevel.error, *args, **kwargs) - - def _handle_info(self, *args, **kwargs): - self._handle(self.MessageLevel.info, *args, **kwargs) - - def _handle_warning(self, *args, **kwargs): - self._handle(self.MessageLevel.warning, *args, **kwargs) - - def getmsg(self): - """Get the only message in self.messages. - - Raises ValueError if there are multiple or no messages. - """ - if len(self.messages) != 1: - raise ValueError("Got {} messages but expected a single " - "one.".format(len(self.messages))) - return self.messages[0] - - def patch(self, module_path): - """Patch message.* in the given module (as a string).""" - self._monkeypatch.setattr(module_path + '.error', self._handle_error) - self._monkeypatch.setattr(module_path + '.info', self._handle_info) - self._monkeypatch.setattr(module_path + '.warning', - self._handle_warning) +@pytest.fixture(scope='session') +def qnam(qapp): + """Session-wide QNetworkAccessManager.""" + from PyQt5.QtNetwork import QNetworkAccessManager + nam = QNetworkAccessManager() + nam.setNetworkAccessible(QNetworkAccessManager.NotAccessible) + return nam @pytest.fixture -def message_mock(monkeypatch): - """Fixture to get a MessageMock.""" - return MessageMock(monkeypatch) +def webpage(qnam): + """Get a new QWebPage object.""" + from PyQt5.QtWebKitWidgets import QWebPage + + page = QWebPage() + page.networkAccessManager().deleteLater() + page.setNetworkAccessManager(qnam) + return page -FakeWindow = collections.namedtuple('FakeWindow', ['registry']) +@pytest.fixture +def webframe(webpage): + """Convenience fixture to get a mainFrame of a QWebPage.""" + return webpage.mainFrame() -@pytest.yield_fixture -def win_registry(): - """Fixture providing a window registry for win_id 0.""" - registry = objreg.ObjectRegistry() - window = FakeWindow(registry) - objreg.window_registry[0] = window - yield registry - del objreg.window_registry[0] +@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 -@pytest.yield_fixture -def tab_registry(win_registry): - """Fixture providing a tab registry for win_id 0.""" - registry = objreg.ObjectRegistry() - objreg.register('tab-registry', registry, scope='window', window=0) - yield registry - objreg.delete('tab-registry', scope='window', window=0) - - -@pytest.yield_fixture(autouse=True) -def caplog_bug_workaround(request): - """WORKAROUND for pytest-capturelog bug. - - https://bitbucket.org/memedough/pytest-capturelog/issues/7/ - - This would lead to LogFailHandler failing after skipped tests as there are - multiple CaptureLogHandlers. - """ - yield - if capturelog_mod is None: - return - - root_logger = logging.getLogger() - caplog_handlers = [h for h in root_logger.handlers - if isinstance(h, capturelog_mod.CaptureLogHandler)] - - for h in caplog_handlers: - root_logger.removeHandler(h) - h.close() + return fake_keyevent diff --git a/tests/helpers/logfail.py b/tests/helpers/logfail.py index b4a6afda5..e3b619645 100644 --- a/tests/helpers/logfail.py +++ b/tests/helpers/logfail.py @@ -65,3 +65,34 @@ class LogFailHandler(logging.Handler): pytest.fail("Got logging message on logger {} with level {}: " "{}!".format(record.name, record.levelname, record.getMessage())) + + +@pytest.yield_fixture(scope='session', autouse=True) +def fail_on_logging(): + handler = LogFailHandler() + logging.getLogger().addHandler(handler) + yield + logging.getLogger().removeHandler(handler) + handler.close() + + +@pytest.yield_fixture(autouse=True) +def caplog_bug_workaround(request): + """WORKAROUND for pytest-capturelog bug. + + https://bitbucket.org/memedough/pytest-capturelog/issues/7/ + + This would lead to LogFailHandler failing after skipped tests as there are + multiple CaptureLogHandlers. + """ + yield + if pytest_capturelog is None: + return + + root_logger = logging.getLogger() + caplog_handlers = [h for h in root_logger.handlers + if isinstance(h, pytest_capturelog.CaptureLogHandler)] + + for h in caplog_handlers: + root_logger.removeHandler(h) + h.close() diff --git a/tests/helpers/messagemock.py b/tests/helpers/messagemock.py new file mode 100644 index 000000000..9bcfc1e1c --- /dev/null +++ b/tests/helpers/messagemock.py @@ -0,0 +1,81 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014-2015 Florian Bruhin (The Compiler) +# +# 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 . + +"""pytest helper to monkeypatch the message module.""" + +import pytest + +import collections + +from qutebrowser.utils import usertypes + + +class MessageMock: + + """Helper object for message_mock. + + Attributes: + _monkeypatch: The pytest monkeypatch fixture. + MessageLevel: An enum with possible message levels. + Message: A namedtuple representing a message. + messages: A list of Message tuples. + """ + + Message = collections.namedtuple('Message', ['level', 'win_id', 'text', + 'immediate']) + MessageLevel = usertypes.enum('Level', ('error', 'info', 'warning')) + + def __init__(self, monkeypatch): + self._monkeypatch = monkeypatch + self.messages = [] + + def _handle(self, level, win_id, text, immediately=False): + self.messages.append(self.Message(level, win_id, text, immediately)) + + def _handle_error(self, *args, **kwargs): + self._handle(self.MessageLevel.error, *args, **kwargs) + + def _handle_info(self, *args, **kwargs): + self._handle(self.MessageLevel.info, *args, **kwargs) + + def _handle_warning(self, *args, **kwargs): + self._handle(self.MessageLevel.warning, *args, **kwargs) + + def getmsg(self): + """Get the only message in self.messages. + + Raises ValueError if there are multiple or no messages. + """ + if len(self.messages) != 1: + raise ValueError("Got {} messages but expected a single " + "one.".format(len(self.messages))) + return self.messages[0] + + def patch(self, module_path): + """Patch message.* in the given module (as a string).""" + self._monkeypatch.setattr(module_path + '.error', self._handle_error) + self._monkeypatch.setattr(module_path + '.info', self._handle_info) + self._monkeypatch.setattr(module_path + '.warning', + self._handle_warning) + + +@pytest.fixture +def message_mock(monkeypatch): + """Fixture to get a MessageMock.""" + return MessageMock(monkeypatch)