tests: Improve MessageMock and use it.

This commit is contained in:
Florian Bruhin 2015-08-18 21:28:22 +02:00
parent 6d1b0ba260
commit e4a0f1972f
5 changed files with 73 additions and 52 deletions

View File

@ -47,12 +47,12 @@ class GUIProcess(QObject):
Args:
cmd: The command which was started.
args: A list of arguments which gets passed.
verbose: Whether to show more messages.
_started: Whether the underlying process is started.
_proc: The underlying QProcess.
_win_id: The window ID this process is used in.
_what: What kind of thing is spawned (process/editor/userscript/...).
Used in messages.
_verbose: Whether to show more messages.
Signals:
error/finished/started signals proxied from QProcess.
@ -67,7 +67,7 @@ class GUIProcess(QObject):
super().__init__(parent)
self._win_id = win_id
self._what = what
self._verbose = verbose
self.verbose = verbose
self._started = False
self.cmd = None
self.args = None
@ -104,7 +104,7 @@ class GUIProcess(QObject):
"{} crashed!".format(self._what.capitalize()),
immediately=True)
elif status == QProcess.NormalExit and code == 0:
if self._verbose:
if self.verbose:
message.info(self._win_id, "{} exited successfully.".format(
self._what.capitalize()))
else:
@ -125,7 +125,7 @@ class GUIProcess(QObject):
raise ValueError("Trying to start a running QProcess!")
self.cmd = cmd
self.args = args
if self._verbose:
if self.verbose:
fake_cmdline = ' '.join(shlex.quote(e) for e in [cmd] + list(args))
message.info(self._win_id, 'Executing: ' + fake_cmdline)

View File

@ -19,43 +19,58 @@
"""pytest helper to monkeypatch the message module."""
import pytest
import logging
import collections
import pytest
from qutebrowser.utils import usertypes
Message = collections.namedtuple('Message', ['level', 'win_id', 'text',
'immediate'])
Level = usertypes.enum('Level', ('error', 'info', 'warning'))
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.
caplog: The pytest-capturelog fixture.
"""
Message = collections.namedtuple('Message', ['level', 'win_id', 'text',
'immediate'])
MessageLevel = usertypes.enum('Level', ('error', 'info', 'warning'))
def __init__(self, monkeypatch):
def __init__(self, monkeypatch, caplog):
self._monkeypatch = monkeypatch
self._caplog = caplog
self.messages = []
def _handle(self, level, win_id, text, immediately=False):
self.messages.append(self.Message(level, win_id, text, immediately))
log_levels = {
Level.error: logging.ERROR,
Level.info: logging.INFO,
Level.warning: logging.WARNING
}
log_level = log_levels[level]
with self._caplog.atLevel(log_level): # needed so we don't fail
logging.getLogger('message').log(log_level, text)
self.messages.append(Message(level, win_id, text, immediately))
def _handle_error(self, *args, **kwargs):
self._handle(self.MessageLevel.error, *args, **kwargs)
self._handle(Level.error, *args, **kwargs)
def _handle_info(self, *args, **kwargs):
self._handle(self.MessageLevel.info, *args, **kwargs)
self._handle(Level.info, *args, **kwargs)
def _handle_warning(self, *args, **kwargs):
self._handle(self.MessageLevel.warning, *args, **kwargs)
self._handle(Level.warning, *args, **kwargs)
def getmsg(self):
"""Get the only message in self.messages.
@ -76,6 +91,6 @@ class MessageMock:
@pytest.fixture
def message_mock(monkeypatch):
def message_mock(monkeypatch, caplog):
"""Fixture to get a MessageMock."""
return MessageMock(monkeypatch)
return MessageMock(monkeypatch, caplog)

View File

@ -21,8 +21,6 @@
"""Fake objects/stubs."""
import logging
from unittest import mock
from PyQt5.QtCore import pyqtSignal, QPoint, QProcess, QObject
@ -276,25 +274,6 @@ class FakeTimer(QObject):
return self._started
class MessageModule:
"""A drop-in replacement for qutebrowser.utils.message."""
# pylint: disable=unused-argument
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)
class ConfigStub:
"""Stub for the config module.

View File

@ -96,9 +96,8 @@ class TestFileHandling:
"""
@pytest.fixture(autouse=True)
def setup(self, monkeypatch, stubs, config_stub):
monkeypatch.setattr('qutebrowser.misc.editor.message',
stubs.MessageModule())
def setup(self, monkeypatch, stubs, config_stub, message_mock):
message_mock.patch('qutebrowser.misc.editor.message')
monkeypatch.setattr('qutebrowser.misc.editor.guiprocess.QProcess',
stubs.fake_qprocess())
config_stub.data = {'general': {'editor': [''],
@ -217,11 +216,10 @@ class TestErrorMessage:
"""
@pytest.yield_fixture(autouse=True)
def setup(self, monkeypatch, stubs, config_stub):
def setup(self, monkeypatch, stubs, config_stub, message_mock):
monkeypatch.setattr('qutebrowser.misc.editor.guiprocess.QProcess',
stubs.fake_qprocess())
monkeypatch.setattr('qutebrowser.misc.editor.message',
stubs.MessageModule())
message_mock.patch('qutebrowser.misc.editor.message')
config_stub.data = {'general': {'editor': [''],
'editor-encoding': 'utf-8'}}
monkeypatch.setattr('qutebrowser.misc.editor.config', config_stub)

View File

@ -27,6 +27,7 @@ import logging
import pytest
from PyQt5.QtCore import QProcess
from helpers import messagemock # pylint: disable=import-error
from qutebrowser.misc import guiprocess
@ -39,9 +40,9 @@ def _py_proc(code):
@pytest.fixture(autouse=True)
def mock_modules(monkeypatch, stubs):
monkeypatch.setattr('qutebrowser.misc.guiprocess.message',
stubs.MessageModule())
def guiprocess_message_mock(message_mock):
message_mock.patch('qutebrowser.misc.guiprocess.message')
return message_mock
@pytest.yield_fixture()
@ -68,13 +69,32 @@ def fake_proc(monkeypatch, stubs):
@pytest.mark.not_frozen
def test_start(proc, qtbot):
def test_start(proc, qtbot, guiprocess_message_mock):
"""Test simply starting a process."""
with qtbot.waitSignals([proc.started, proc.finished], raising=True,
timeout=10000):
argv = _py_proc("import sys; print('test'); sys.exit(0)")
proc.start(*argv)
assert not guiprocess_message_mock.messages
assert bytes(proc._proc.readAll()).rstrip() == b'test'
@pytest.mark.not_frozen
def test_start_verbose(proc, qtbot, guiprocess_message_mock):
"""Test starting a process verbosely."""
proc.verbose = True
with qtbot.waitSignals([proc.started, proc.finished], raising=True,
timeout=10000):
argv = _py_proc("import sys; print('test'); sys.exit(0)")
proc.start(*argv)
msgs = guiprocess_message_mock.messages
assert msgs[0].level == messagemock.Level.info
assert msgs[1].level == messagemock.Level.info
assert msgs[0].text.startswith("Executing:")
assert msgs[1].text == "Test exited successfully."
assert bytes(proc._proc.readAll()).rstrip() == b'test'
@ -120,14 +140,23 @@ def test_cmd_args(fake_proc):
assert (fake_proc.cmd, fake_proc.args) == (cmd, args)
def test_error(qtbot, proc, caplog):
def test_error(qtbot, proc, caplog, guiprocess_message_mock):
"""Test the process emitting an error."""
with caplog.atLevel(logging.ERROR, 'message'):
with qtbot.waitSignal(proc.error, raising=True):
proc.start('this_does_not_exist_either', [])
msg = guiprocess_message_mock.getmsg()
assert msg.level == messagemock.Level.error
expected_msg = "Error while spawning test: The process failed to start."
assert msg.text == expected_msg
@pytest.mark.not_frozen
def test_exit_unsuccessful(qtbot, proc):
def test_exit_unsuccessful(qtbot, proc, guiprocess_message_mock):
with qtbot.waitSignal(proc.finished, raising=True, timeout=10000):
proc.start(*_py_proc('import sys; sys.exit(0)'))
proc.start(*_py_proc('import sys; sys.exit(1)'))
msg = guiprocess_message_mock.getmsg()
assert msg.level == messagemock.Level.error
assert msg.text == "Test exited with status 1."