Capture qutebrowser/httpbin output separately.

When using print and relying on pytest to capture it as stdout, we ran into
this pytest/pytest-qt issue:

https://github.com/pytest-dev/pytest-qt/issues/113

Now we use our own capturing mechanism instead, which also means we get nicer
output.

Fixes #1122.
This commit is contained in:
Florian Bruhin 2015-12-16 20:17:29 +01:00
parent 7f83c6c5c8
commit bba6589e19
4 changed files with 38 additions and 7 deletions

View File

@ -23,3 +23,4 @@
from webserver import httpbin, httpbin_after_test from webserver import httpbin, httpbin_after_test
from quteprocess import quteproc_process, quteproc from quteprocess import quteproc_process, quteproc
from testprocess import pytest_runtest_makereport

View File

@ -174,7 +174,7 @@ class QuteProc(testprocess.Process):
# pylint: disable=no-member # pylint: disable=no-member
if (log_line.loglevel in ['INFO', 'WARNING', 'ERROR'] or if (log_line.loglevel in ['INFO', 'WARNING', 'ERROR'] or
pytest.config.getoption('--verbose')): pytest.config.getoption('--verbose')):
print(line) self._log(line)
start_okay_message_load = ( start_okay_message_load = (
"load status for <qutebrowser.browser.webview.WebView tab_id=0 " "load status for <qutebrowser.browser.webview.WebView tab_id=0 "
@ -306,7 +306,7 @@ class QuteProc(testprocess.Process):
with open(session, encoding='utf-8') as f: with open(session, encoding='utf-8') as f:
data = f.read() data = f.read()
print(data) self._log(data)
return yaml.load(data) return yaml.load(data)
def get_content(self, plain=True): def get_content(self, plain=True):
@ -341,8 +341,9 @@ def quteproc_process(qapp, httpbin, request):
@pytest.yield_fixture @pytest.yield_fixture
def quteproc(quteproc_process, httpbin): def quteproc(quteproc_process, httpbin, request):
"""Per-test qutebrowser fixture which uses the per-file process.""" """Per-test qutebrowser fixture which uses the per-file process."""
request.node._quteproc_log = quteproc_process.captured_log
quteproc_process.before_test() quteproc_process.before_test()
yield quteproc_process yield quteproc_process
quteproc_process.after_test() quteproc_process.after_test()

View File

@ -23,6 +23,7 @@ import re
import os import os
import time import time
import pytest
import pytestqt.plugin import pytestqt.plugin
from PyQt5.QtCore import pyqtSlot, pyqtSignal, QProcess, QObject, QElapsedTimer from PyQt5.QtCore import pyqtSlot, pyqtSignal, QProcess, QObject, QElapsedTimer
from PyQt5.QtTest import QSignalSpy from PyQt5.QtTest import QSignalSpy
@ -71,6 +72,28 @@ class Line:
return '{}({!r})'.format(self.__class__.__name__, self.data) return '{}({!r})'.format(self.__class__.__name__, self.data)
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
"""Add qutebrowser/httpbin sections to captured output if a test failed."""
outcome = yield
if call.when != 'call':
return
report = outcome.get_result()
if report.passed:
return
quteproc_log = getattr(item, '_quteproc_log', None)
httpbin_log = getattr(item, '_httpbin_log', None)
if quteproc_log is not None:
report.longrepr.addsection("qutebrowser output",
'\n'.join(quteproc_log))
if httpbin_log is not None:
report.longrepr.addsection("httpbin output",
'\n'.join(httpbin_log))
class Process(QObject): class Process(QObject):
"""Abstraction over a running test subprocess process. """Abstraction over a running test subprocess process.
@ -93,11 +116,16 @@ class Process(QObject):
def __init__(self, parent=None): def __init__(self, parent=None):
super().__init__(parent) super().__init__(parent)
self.captured_log = []
self._invalid = [] self._invalid = []
self._data = [] self._data = []
self.proc = QProcess() self.proc = QProcess()
self.proc.setReadChannel(QProcess.StandardError) self.proc.setReadChannel(QProcess.StandardError)
def _log(self, line):
"""Add the given line to the captured log output."""
self.captured_log.append(line)
def _parse_line(self, line): def _parse_line(self, line):
"""Parse the given line from the log. """Parse the given line from the log.
@ -146,12 +174,12 @@ class Process(QObject):
parsed = self._parse_line(line) parsed = self._parse_line(line)
except InvalidLine: except InvalidLine:
self._invalid.append(line) self._invalid.append(line)
print("INVALID: {}".format(line)) self._log("INVALID: {}".format(line))
continue continue
if parsed is None: if parsed is None:
if self._invalid: if self._invalid:
print("IGNORED: {}".format(line)) self._log("IGNORED: {}".format(line))
else: else:
self._data.append(parsed) self._data.append(parsed)
self.new_data.emit(parsed) self.new_data.emit(parsed)

View File

@ -129,7 +129,7 @@ class HTTPBin(testprocess.Process):
return [r for r in requests if r.path != '/favicon.ico'] return [r for r in requests if r.path != '/favicon.ico']
def _parse_line(self, line): def _parse_line(self, line):
print(line) self._log(line)
if line == (' * Running on http://127.0.0.1:{}/ (Press CTRL+C to ' if line == (' * Running on http://127.0.0.1:{}/ (Press CTRL+C to '
'quit)'.format(self.port)): 'quit)'.format(self.port)):
self.ready.emit() self.ready.emit()
@ -164,7 +164,8 @@ def httpbin(qapp):
@pytest.yield_fixture(autouse=True) @pytest.yield_fixture(autouse=True)
def httpbin_after_test(httpbin): def httpbin_after_test(httpbin, request):
"""Fixture to clean httpbin request list after each test.""" """Fixture to clean httpbin request list after each test."""
request.node._httpbin_log = httpbin.captured_log
yield yield
httpbin.after_test() httpbin.after_test()