diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 7372bc092..7be075d2a 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -84,6 +84,8 @@ Changed - `general -> log-javascript-console` got changed from a boolean to an option taking a loglevel (`none`, `info`, `debug`). - `:tab-move +/-` now wraps around if `tabs -> wrap` is `true`. +- When a subprocess (like launched by `:spawn`) fails, its stdout/stderr is now + logged to the console. Deprecated ~~~~~~~~~~ diff --git a/qutebrowser/misc/guiprocess.py b/qutebrowser/misc/guiprocess.py index 2c4c4f0a2..a6d1dc528 100644 --- a/qutebrowser/misc/guiprocess.py +++ b/qutebrowser/misc/guiprocess.py @@ -109,9 +109,18 @@ class GUIProcess(QObject): self._what.capitalize())) else: assert status == QProcess.NormalExit + # We call this 'status' here as it makes more sense to the user - + # it's actually 'code'. message.error(self._win_id, "{} exited with status {}.".format( self._what.capitalize(), code)) + stderr = bytes(self._proc.readAllStandardError()).decode('utf-8') + stdout = bytes(self._proc.readAllStandardOutput()).decode('utf-8') + if stdout: + log.procs.error("Process stdout:\n" + stdout.strip()) + if stderr: + log.procs.error("Process stderr:\n" + stderr.strip()) + @pyqtSlot() def on_started(self): """Called when the process started successfully.""" diff --git a/tests/unit/misc/test_guiprocess.py b/tests/unit/misc/test_guiprocess.py index 0682238fc..d38d708e3 100644 --- a/tests/unit/misc/test_guiprocess.py +++ b/tests/unit/misc/test_guiprocess.py @@ -93,6 +93,8 @@ def test_start_env(monkeypatch, qtbot, py_proc): argv = py_proc(""" import os import json + import sys + env = dict(os.environ) print(json.dumps(env)) sys.exit(0) @@ -201,3 +203,33 @@ def test_exit_unsuccessful(qtbot, proc, guiprocess_message_mock, py_proc): msg = guiprocess_message_mock.getmsg(guiprocess_message_mock.Level.error) assert msg.text == "Testprocess exited with status 1." + + +@pytest.mark.not_frozen +@pytest.mark.parametrize('stream', ['stdout', 'stderr']) +def test_exit_unsuccessful_output(qtbot, proc, caplog, py_proc, stream): + """When a process fails, its output should be logged.""" + with caplog.atLevel(logging.ERROR): + with qtbot.waitSignal(proc.finished, raising=True, timeout=10000): + proc.start(*py_proc(""" + import sys + print("test", file=sys.{}) + sys.exit(1) + """.format(stream))) + assert len(caplog.records) == 2 + assert caplog.records[1].msg == 'Process {}:\ntest'.format(stream) + + +@pytest.mark.parametrize('stream', ['stdout', 'stderr']) +def test_exit_successful_output(qtbot, proc, py_proc, stream): + """When a process suceeds, no output should be logged. + + The test doesn't actually check the log as it'd fail because of the error + logging. + """ + with qtbot.waitSignal(proc.finished, raising=True, timeout=10000): + proc.start(*py_proc(""" + import sys + print("test", file=sys.{}) + sys.exit(0) + """.format(stream)))