tests: Improve MessageMock and use it.
This commit is contained in:
parent
6d1b0ba260
commit
e4a0f1972f
@ -47,12 +47,12 @@ class GUIProcess(QObject):
|
|||||||
Args:
|
Args:
|
||||||
cmd: The command which was started.
|
cmd: The command which was started.
|
||||||
args: A list of arguments which gets passed.
|
args: A list of arguments which gets passed.
|
||||||
|
verbose: Whether to show more messages.
|
||||||
_started: Whether the underlying process is started.
|
_started: Whether the underlying process is started.
|
||||||
_proc: The underlying QProcess.
|
_proc: The underlying QProcess.
|
||||||
_win_id: The window ID this process is used in.
|
_win_id: The window ID this process is used in.
|
||||||
_what: What kind of thing is spawned (process/editor/userscript/...).
|
_what: What kind of thing is spawned (process/editor/userscript/...).
|
||||||
Used in messages.
|
Used in messages.
|
||||||
_verbose: Whether to show more messages.
|
|
||||||
|
|
||||||
Signals:
|
Signals:
|
||||||
error/finished/started signals proxied from QProcess.
|
error/finished/started signals proxied from QProcess.
|
||||||
@ -67,7 +67,7 @@ class GUIProcess(QObject):
|
|||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self._win_id = win_id
|
self._win_id = win_id
|
||||||
self._what = what
|
self._what = what
|
||||||
self._verbose = verbose
|
self.verbose = verbose
|
||||||
self._started = False
|
self._started = False
|
||||||
self.cmd = None
|
self.cmd = None
|
||||||
self.args = None
|
self.args = None
|
||||||
@ -104,7 +104,7 @@ class GUIProcess(QObject):
|
|||||||
"{} crashed!".format(self._what.capitalize()),
|
"{} crashed!".format(self._what.capitalize()),
|
||||||
immediately=True)
|
immediately=True)
|
||||||
elif status == QProcess.NormalExit and code == 0:
|
elif status == QProcess.NormalExit and code == 0:
|
||||||
if self._verbose:
|
if self.verbose:
|
||||||
message.info(self._win_id, "{} exited successfully.".format(
|
message.info(self._win_id, "{} exited successfully.".format(
|
||||||
self._what.capitalize()))
|
self._what.capitalize()))
|
||||||
else:
|
else:
|
||||||
@ -125,7 +125,7 @@ class GUIProcess(QObject):
|
|||||||
raise ValueError("Trying to start a running QProcess!")
|
raise ValueError("Trying to start a running QProcess!")
|
||||||
self.cmd = cmd
|
self.cmd = cmd
|
||||||
self.args = args
|
self.args = args
|
||||||
if self._verbose:
|
if self.verbose:
|
||||||
fake_cmdline = ' '.join(shlex.quote(e) for e in [cmd] + list(args))
|
fake_cmdline = ' '.join(shlex.quote(e) for e in [cmd] + list(args))
|
||||||
message.info(self._win_id, 'Executing: ' + fake_cmdline)
|
message.info(self._win_id, 'Executing: ' + fake_cmdline)
|
||||||
|
|
||||||
|
@ -19,43 +19,58 @@
|
|||||||
|
|
||||||
"""pytest helper to monkeypatch the message module."""
|
"""pytest helper to monkeypatch the message module."""
|
||||||
|
|
||||||
import pytest
|
import logging
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from qutebrowser.utils import usertypes
|
from qutebrowser.utils import usertypes
|
||||||
|
|
||||||
|
|
||||||
|
Message = collections.namedtuple('Message', ['level', 'win_id', 'text',
|
||||||
|
'immediate'])
|
||||||
|
|
||||||
|
|
||||||
|
Level = usertypes.enum('Level', ('error', 'info', 'warning'))
|
||||||
|
|
||||||
|
|
||||||
class MessageMock:
|
class MessageMock:
|
||||||
|
|
||||||
"""Helper object for message_mock.
|
"""Helper object for message_mock.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
_monkeypatch: The pytest monkeypatch fixture.
|
_monkeypatch: The pytest monkeypatch fixture.
|
||||||
MessageLevel: An enum with possible message levels.
|
|
||||||
Message: A namedtuple representing a message.
|
Message: A namedtuple representing a message.
|
||||||
messages: A list of Message tuples.
|
messages: A list of Message tuples.
|
||||||
|
caplog: The pytest-capturelog fixture.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
Message = collections.namedtuple('Message', ['level', 'win_id', 'text',
|
def __init__(self, monkeypatch, caplog):
|
||||||
'immediate'])
|
|
||||||
MessageLevel = usertypes.enum('Level', ('error', 'info', 'warning'))
|
|
||||||
|
|
||||||
def __init__(self, monkeypatch):
|
|
||||||
self._monkeypatch = monkeypatch
|
self._monkeypatch = monkeypatch
|
||||||
|
self._caplog = caplog
|
||||||
self.messages = []
|
self.messages = []
|
||||||
|
|
||||||
def _handle(self, level, win_id, text, immediately=False):
|
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):
|
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):
|
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):
|
def _handle_warning(self, *args, **kwargs):
|
||||||
self._handle(self.MessageLevel.warning, *args, **kwargs)
|
self._handle(Level.warning, *args, **kwargs)
|
||||||
|
|
||||||
def getmsg(self):
|
def getmsg(self):
|
||||||
"""Get the only message in self.messages.
|
"""Get the only message in self.messages.
|
||||||
@ -76,6 +91,6 @@ class MessageMock:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def message_mock(monkeypatch):
|
def message_mock(monkeypatch, caplog):
|
||||||
"""Fixture to get a MessageMock."""
|
"""Fixture to get a MessageMock."""
|
||||||
return MessageMock(monkeypatch)
|
return MessageMock(monkeypatch, caplog)
|
||||||
|
@ -21,8 +21,6 @@
|
|||||||
|
|
||||||
"""Fake objects/stubs."""
|
"""Fake objects/stubs."""
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from PyQt5.QtCore import pyqtSignal, QPoint, QProcess, QObject
|
from PyQt5.QtCore import pyqtSignal, QPoint, QProcess, QObject
|
||||||
@ -276,25 +274,6 @@ class FakeTimer(QObject):
|
|||||||
return self._started
|
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:
|
class ConfigStub:
|
||||||
|
|
||||||
"""Stub for the config module.
|
"""Stub for the config module.
|
||||||
|
@ -96,9 +96,8 @@ class TestFileHandling:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def setup(self, monkeypatch, stubs, config_stub):
|
def setup(self, monkeypatch, stubs, config_stub, message_mock):
|
||||||
monkeypatch.setattr('qutebrowser.misc.editor.message',
|
message_mock.patch('qutebrowser.misc.editor.message')
|
||||||
stubs.MessageModule())
|
|
||||||
monkeypatch.setattr('qutebrowser.misc.editor.guiprocess.QProcess',
|
monkeypatch.setattr('qutebrowser.misc.editor.guiprocess.QProcess',
|
||||||
stubs.fake_qprocess())
|
stubs.fake_qprocess())
|
||||||
config_stub.data = {'general': {'editor': [''],
|
config_stub.data = {'general': {'editor': [''],
|
||||||
@ -217,11 +216,10 @@ class TestErrorMessage:
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@pytest.yield_fixture(autouse=True)
|
@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',
|
monkeypatch.setattr('qutebrowser.misc.editor.guiprocess.QProcess',
|
||||||
stubs.fake_qprocess())
|
stubs.fake_qprocess())
|
||||||
monkeypatch.setattr('qutebrowser.misc.editor.message',
|
message_mock.patch('qutebrowser.misc.editor.message')
|
||||||
stubs.MessageModule())
|
|
||||||
config_stub.data = {'general': {'editor': [''],
|
config_stub.data = {'general': {'editor': [''],
|
||||||
'editor-encoding': 'utf-8'}}
|
'editor-encoding': 'utf-8'}}
|
||||||
monkeypatch.setattr('qutebrowser.misc.editor.config', config_stub)
|
monkeypatch.setattr('qutebrowser.misc.editor.config', config_stub)
|
||||||
|
@ -27,6 +27,7 @@ import logging
|
|||||||
import pytest
|
import pytest
|
||||||
from PyQt5.QtCore import QProcess
|
from PyQt5.QtCore import QProcess
|
||||||
|
|
||||||
|
from helpers import messagemock # pylint: disable=import-error
|
||||||
from qutebrowser.misc import guiprocess
|
from qutebrowser.misc import guiprocess
|
||||||
|
|
||||||
|
|
||||||
@ -39,9 +40,9 @@ def _py_proc(code):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def mock_modules(monkeypatch, stubs):
|
def guiprocess_message_mock(message_mock):
|
||||||
monkeypatch.setattr('qutebrowser.misc.guiprocess.message',
|
message_mock.patch('qutebrowser.misc.guiprocess.message')
|
||||||
stubs.MessageModule())
|
return message_mock
|
||||||
|
|
||||||
|
|
||||||
@pytest.yield_fixture()
|
@pytest.yield_fixture()
|
||||||
@ -68,13 +69,32 @@ def fake_proc(monkeypatch, stubs):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.not_frozen
|
@pytest.mark.not_frozen
|
||||||
def test_start(proc, qtbot):
|
def test_start(proc, qtbot, guiprocess_message_mock):
|
||||||
"""Test simply starting a process."""
|
"""Test simply starting a process."""
|
||||||
with qtbot.waitSignals([proc.started, proc.finished], raising=True,
|
with qtbot.waitSignals([proc.started, proc.finished], raising=True,
|
||||||
timeout=10000):
|
timeout=10000):
|
||||||
argv = _py_proc("import sys; print('test'); sys.exit(0)")
|
argv = _py_proc("import sys; print('test'); sys.exit(0)")
|
||||||
proc.start(*argv)
|
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'
|
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)
|
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."""
|
"""Test the process emitting an error."""
|
||||||
with caplog.atLevel(logging.ERROR, 'message'):
|
with caplog.atLevel(logging.ERROR, 'message'):
|
||||||
with qtbot.waitSignal(proc.error, raising=True):
|
with qtbot.waitSignal(proc.error, raising=True):
|
||||||
proc.start('this_does_not_exist_either', [])
|
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
|
@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):
|
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."
|
||||||
|
Loading…
Reference in New Issue
Block a user