Improve tests for guiprocess

This commit is contained in:
Florian Bruhin 2021-03-22 17:32:47 +01:00
parent 7750a981b5
commit 1ddbb2da9e
2 changed files with 137 additions and 1 deletions

View File

@ -287,6 +287,9 @@ class FakeWebTab(browsertab.AbstractTab):
def renderer_process_pid(self):
return None
def load_url(self, url):
self._url = url
class FakeSignal:

View File

@ -19,13 +19,15 @@
"""Tests for qutebrowser.misc.guiprocess."""
import sys
import logging
import pytest
from PyQt5.QtCore import QProcess
from PyQt5.QtCore import QProcess, QUrl
from qutebrowser.misc import guiprocess
from qutebrowser.utils import usertypes, utils
from qutebrowser.api import cmdutils
@pytest.fixture()
@ -51,6 +53,59 @@ def fake_proc(monkeypatch, stubs):
return p
class TestProcessCommand:
@pytest.fixture
def tab(self, fake_web_tab):
return fake_web_tab()
def test_no_process(self, tab, monkeypatch):
monkeypatch.setattr(guiprocess, 'last_pid', None)
with pytest.raises(cmdutils.CommandError, match='No process executed yet!'):
guiprocess.process(tab)
def test_last_pid(self, tab, monkeypatch, fake_proc):
monkeypatch.setattr(guiprocess, 'last_pid', 1234)
monkeypatch.setitem(guiprocess.all_processes, 1234, fake_proc)
guiprocess.process(tab)
assert tab.url() == QUrl('qute://process/1234')
def test_explicit_pid(self, tab, monkeypatch, fake_proc):
monkeypatch.setattr(guiprocess, 'last_pid', 1234)
monkeypatch.setitem(guiprocess.all_processes, 5678, fake_proc)
guiprocess.process(tab, 5678)
assert tab.url() == QUrl('qute://process/5678')
def test_inexistent_pid(self, tab):
with pytest.raises(cmdutils.CommandError, match='No process found with pid 1337'):
guiprocess.process(tab, 1337)
def test_terminate(self, tab, monkeypatch, fake_proc):
monkeypatch.setitem(guiprocess.all_processes, 1234, fake_proc)
guiprocess.process(tab, 1234, 'terminate')
fake_proc._proc.terminate.assert_called_with()
fake_proc._proc.kill.assert_not_called()
def test_kill(self, tab, monkeypatch, fake_proc):
monkeypatch.setitem(guiprocess.all_processes, 1234, fake_proc)
guiprocess.process(tab, 1234, 'kill')
fake_proc._proc.kill.assert_called_with()
fake_proc._proc.terminate.assert_not_called()
def test_not_started(proc):
assert str(proc.outcome) == 'Testprocess did not start.'
assert proc.outcome.state_str() == 'not started'
assert not proc.outcome.was_successful()
assert not proc.outcome.running
assert proc.outcome.status is None
assert proc.outcome.code is None
def test_start(proc, qtbot, message_mock, py_proc):
"""Test simply starting a process."""
with qtbot.wait_signals([proc.started, proc.finished], timeout=10000,
@ -59,7 +114,13 @@ def test_start(proc, qtbot, message_mock, py_proc):
proc.start(cmd, args)
assert not message_mock.messages
assert not proc.outcome.running
assert proc.outcome.status == QProcess.NormalExit
assert proc.outcome.code == 0
assert str(proc.outcome) == 'Testprocess exited successfully.'
assert proc.outcome.state_str() == 'successful'
assert proc.outcome.was_successful()
def test_start_verbose(proc, qtbot, message_mock, py_proc):
@ -127,6 +188,37 @@ def test_start_output_message(proc, qtbot, caplog, message_mock, py_proc,
assert proc.stderr.strip() == "stderr text", proc.stderr
@pytest.mark.parametrize('line1, line2, expected1, expected2', [
('First line\n', 'Second line\n', 'First line', 'First line\nSecond line'),
('First line', '\rSecond line', 'First line', 'Second line'),
('First line\n', '\rSecond line', 'First line', 'First line\nSecond line'),
('First line\nSecond line\nThird line', '\rNew line', 'First line\nSecond line\nThird line', 'First line\nSecond line\nNew line'),
('First line', 'Second line\rThird line', 'First line', 'Third line'),
])
def test_live_messages_output(qtbot, proc, py_proc, message_mock,
line1, line2, expected1, expected2):
proc._output_messages = True
cmd, args = py_proc(r"""
import time, sys
print(sys.argv[1], flush=True, end='')
time.sleep(0.5)
print(sys.argv[2], flush=True, end='')
""")
args += [line1, line2]
with qtbot.wait_signal(proc.finished, timeout=5000):
proc.start(cmd, args)
assert len(message_mock.messages) == 3
assert all(msg.level == usertypes.MessageLevel.info
for msg in message_mock.messages)
assert message_mock.messages[0].text == expected1
assert message_mock.messages[1].text == expected2
assert message_mock.messages[2].text == expected2
def test_start_env(monkeypatch, qtbot, py_proc):
monkeypatch.setenv('QUTEBROWSER_TEST_1', '1')
env = {'QUTEBROWSER_TEST_2': '2'}
@ -213,6 +305,18 @@ def test_start_logging(fake_proc, caplog):
]
def test_running(qtbot, proc, py_proc):
"""Test proc.outcome while the process is still running."""
with qtbot.wait_signal(proc.started, timeout=5000):
proc.start(*py_proc("import time; time.sleep(10)"))
assert proc.outcome.running
assert proc.outcome.status is None
assert proc.outcome.code is None
assert str(proc.outcome) == 'Testprocess is running.'
assert proc.outcome.state_str() == 'running'
assert not proc.outcome.was_successful()
def test_error(qtbot, proc, caplog, message_mock):
"""Test the process emitting an error."""
with caplog.at_level(logging.ERROR, 'message'):
@ -227,6 +331,13 @@ def test_error(qtbot, proc, caplog, message_mock):
assert msg.text.endswith(
"(Hint: Make sure 'this_does_not_exist_either' exists and is executable)")
assert not proc.outcome.running
assert proc.outcome.status is None
assert proc.outcome.code is None
assert str(proc.outcome) == 'Testprocess did not start.'
assert proc.outcome.state_str() == 'not started'
assert not proc.outcome.was_successful()
def test_exit_unsuccessful(qtbot, proc, message_mock, py_proc, caplog):
with caplog.at_level(logging.ERROR):
@ -237,6 +348,13 @@ def test_exit_unsuccessful(qtbot, proc, message_mock, py_proc, caplog):
expected = "Testprocess exited with status 1. See :process for details."
assert msg.text == expected
assert not proc.outcome.running
assert proc.outcome.status == QProcess.NormalExit
assert proc.outcome.code == 1
assert str(proc.outcome) == 'Testprocess exited with status 1.'
assert proc.outcome.state_str() == 'unsuccessful'
assert not proc.outcome.was_successful()
def test_exit_crash(qtbot, proc, message_mock, py_proc, caplog):
with caplog.at_level(logging.ERROR):
@ -253,6 +371,12 @@ def test_exit_crash(qtbot, proc, message_mock, py_proc, caplog):
msg = message_mock.getmsg(usertypes.MessageLevel.error)
assert msg.text == expected
assert not proc.outcome.running
assert proc.outcome.status == QProcess.CrashExit
assert str(proc.outcome) == 'Testprocess crashed.'
assert proc.outcome.state_str() == 'crashed'
assert not proc.outcome.was_successful()
@pytest.mark.parametrize('stream', ['stdout', 'stderr'])
def test_exit_unsuccessful_output(qtbot, proc, caplog, py_proc, stream):
@ -298,3 +422,12 @@ def test_stdout_not_decodable(proc, qtbot, message_mock, py_proc):
assert not message_mock.messages
assert proc.stdout == "A\ufffdB"
def test_str_unknown(proc):
assert str(proc) == '<unknown testprocess command>'
def test_str(proc, py_proc):
proc.start(*py_proc("import sys"))
assert str(proc) == f"{sys.executable} -c 'import sys'"