diff --git a/tests/end2end/fixtures/quteprocess.py b/tests/end2end/fixtures/quteprocess.py index 60b0c14fe..9f0e2f6ee 100644 --- a/tests/end2end/fixtures/quteprocess.py +++ b/tests/end2end/fixtures/quteprocess.py @@ -140,17 +140,14 @@ class QuteProc(testprocess.Process): """A running qutebrowser process used for tests. Attributes: - _delay: Delay to wait between commands. _ipc_socket: The IPC socket of the started instance. - _httpbin: The HTTPBin webserver. _webengine: Whether to use QtWebEngine basedir: The base directory for this instance. + _request: The request object for the current test. _focus_ready: Whether the main window got focused. _load_ready: Whether the about:blank page got loaded. - _profile: If True, do profiling of the subprocesses. _instance_id: A unique ID for this QuteProc instance _run_counter: A counter to get a unique ID for each run. - _config: The pytest config object Signals: got_error: Emitted when there was an error log line. @@ -161,20 +158,16 @@ class QuteProc(testprocess.Process): KEYS = ['timestamp', 'loglevel', 'category', 'module', 'function', 'line', 'message'] - def __init__(self, httpbin, delay, *, webengine=False, profile=False, - config=None, parent=None): + def __init__(self, request, *, parent=None): super().__init__(parent) - self._webengine = webengine - self._profile = profile - self._delay = delay - self._httpbin = httpbin + self._webengine = request.config.getoption('--qute-bdd-webengine') self._ipc_socket = None self.basedir = None self._focus_ready = False self._load_ready = False self._instance_id = next(instance_counter) self._run_counter = itertools.count() - self._config = config + self._request = request def _is_ready(self, what): """Called by _parse_line if loading/focusing is done. @@ -204,7 +197,7 @@ class QuteProc(testprocess.Process): else: raise - log_line.use_color = self._config.getoption('--color') != 'no' + log_line.use_color = self._request.config.getoption('--color') != 'no' self._log(log_line) start_okay_message_load = ( @@ -244,15 +237,16 @@ class QuteProc(testprocess.Process): return log_line def _executable_args(self): + profile = self._request.config.getoption('--qute-profile-subprocs') if hasattr(sys, 'frozen'): - if self._profile: + if profile: raise Exception("Can't profile with sys.frozen!") executable = os.path.join(os.path.dirname(sys.executable), 'qutebrowser') args = [] else: executable = sys.executable - if self._profile: + if profile: profile_dir = os.path.join(os.getcwd(), 'prof') profile_id = '{}_{}'.format(self._instance_id, next(self._run_counter)) @@ -283,9 +277,10 @@ class QuteProc(testprocess.Process): if path.startswith('about:') or path.startswith('qute:'): return path else: + httpbin = self._request.getfuncargvalue('httpbin') return '{}://localhost:{}/{}'.format( 'https' if https else 'http', - self._httpbin.port if port is None else port, + httpbin.port if port is None else port, path if path != '/' else '') def wait_for_js(self, message): @@ -341,20 +336,20 @@ class QuteProc(testprocess.Process): for sect, opt, value in settings: self.set_setting(sect, opt, value) - def after_test(self, did_fail): - """Handle unexpected/skip logging and clean up after each test. - - Args: - did_fail: Set if the main test failed already, then logged errors - are ignored. - """ + def after_test(self): + """Handle unexpected/skip logging and clean up after each test.""" __tracebackhide__ = True bad_msgs = [msg for msg in self._data if self._is_error_logline(msg) and not msg.expected] - if did_fail: - super().after_test() - return + try: + call = self._request.node.rep_call + except AttributeError: + pass + else: + if call.failed or hasattr(call, 'wasxfail'): + super().after_test() + return try: if bad_msgs: @@ -370,7 +365,8 @@ class QuteProc(testprocess.Process): def send_ipc(self, commands, target_arg=''): """Send a raw command to the running IPC socket.""" - time.sleep(self._delay / 1000) + delay = self._request.config.getoption('--qute-delay') + time.sleep(delay / 1000) assert self._ipc_socket is not None ipc.send_to_running_instance(self._ipc_socket, commands, target_arg) @@ -604,11 +600,8 @@ def _xpath_escape(text): @pytest.yield_fixture(scope='module') def quteproc_process(qapp, httpbin, request): """Fixture for qutebrowser process which is started once per file.""" - delay = request.config.getoption('--qute-delay') - profile = request.config.getoption('--qute-profile-subprocs') - webengine = request.config.getoption('--qute-bdd-webengine') - proc = QuteProc(httpbin, delay, webengine=webengine, profile=profile, - config=request.config) + # Passing request so it has an initial config + proc = QuteProc(request) proc.start() yield proc proc.terminate() @@ -619,24 +612,17 @@ def quteproc(quteproc_process, httpbin, request): """Per-test qutebrowser fixture which uses the per-file process.""" request.node._quteproc_log = quteproc_process.captured_log quteproc_process.before_test() + quteproc_process.request = request yield quteproc_process - call = request.node.rep_call - did_fail = call.failed or hasattr(call, 'wasxfail') - quteproc_process.after_test(did_fail=did_fail) + quteproc_process.after_test() @pytest.yield_fixture def quteproc_new(qapp, httpbin, request): """Per-test qutebrowser process to test invocations.""" - delay = request.config.getoption('--qute-delay') - profile = request.config.getoption('--qute-profile-subprocs') - webengine = request.config.getoption('--qute-bdd-webengine') - proc = QuteProc(httpbin, delay, webengine=webengine, profile=profile, - config=request.config) + proc = QuteProc(request) request.node._quteproc_log = proc.captured_log # Not calling before_test here as that would start the process yield proc - call = request.node.rep_call - did_fail = call.failed or hasattr(call, 'wasxfail') - proc.after_test(did_fail=did_fail) + proc.after_test() proc.terminate() diff --git a/tests/end2end/fixtures/test_quteprocess.py b/tests/end2end/fixtures/test_quteprocess.py index 374421089..9511f76d4 100644 --- a/tests/end2end/fixtures/test_quteprocess.py +++ b/tests/end2end/fixtures/test_quteprocess.py @@ -22,6 +22,7 @@ import logging import datetime import json +import collections import pytest @@ -29,27 +30,75 @@ from end2end.fixtures import quteprocess, testprocess from qutebrowser.utils import log +class FakeRepCall: + + """Fake for request.node.rep_call.""" + + def __init__(self): + self.failed = False + + +class FakeConfig: + + """Fake for request.config.""" + + ARGS = { + '--qute-delay': 0, + '--color': True, + } + + def getoption(self, name): + return self.ARGS[name] + + +class FakeRequest: + + """Fake for request.""" + + def __init__(self, node, config, httpbin): + self.node = node + self.config = config + self._httpbin = httpbin + + def getfuncargvalue(self, name): + assert name == 'httpbin' + return self._httpbin + + +@pytest.fixture +def request_mock(quteproc, monkeypatch, httpbin): + """Patch out a pytest request.""" + fake_call = FakeRepCall() + fake_config = FakeConfig() + fake_node = collections.namedtuple('FakeNode', ['rep_call'])(fake_call) + fake_request = FakeRequest(fake_node, fake_config, httpbin) + assert not hasattr(fake_request.node.rep_call, 'wasxfail') + monkeypatch.setattr(quteproc, '_request', fake_request) + return fake_request + + @pytest.mark.parametrize('cmd', [ ':message-error test', ':jseval console.log("[FAIL] test");' ]) -def test_quteproc_error_message(qtbot, quteproc, cmd): +def test_quteproc_error_message(qtbot, quteproc, cmd, request_mock): """Make sure the test fails with an unexpected error message.""" with qtbot.waitSignal(quteproc.got_error): quteproc.send_cmd(cmd) # Usually we wouldn't call this from inside a test, but here we force the # error to occur during the test rather than at teardown time. with pytest.raises(pytest.fail.Exception): - quteproc.after_test(did_fail=False) + quteproc.after_test() -def test_quteproc_error_message_did_fail(qtbot, quteproc): +def test_quteproc_error_message_did_fail(qtbot, quteproc, request_mock): """Make sure the test does not fail on teardown if the main test failed.""" + request_mock.node.rep_call.failed = True with qtbot.waitSignal(quteproc.got_error): quteproc.send_cmd(':message-error test') # Usually we wouldn't call this from inside a test, but here we force the # error to occur during the test rather than at teardown time. - quteproc.after_test(did_fail=True) + quteproc.after_test() def test_quteproc_skip_via_js(qtbot, quteproc): @@ -59,7 +108,7 @@ def test_quteproc_skip_via_js(qtbot, quteproc): # Usually we wouldn't call this from inside a test, but here we force # the error to occur during the test rather than at teardown time. - quteproc.after_test(did_fail=False) + quteproc.after_test() assert str(excinfo.value) == 'test' @@ -83,7 +132,7 @@ def test_quteprocess_quitting(qtbot, quteproc_process): with qtbot.waitSignal(quteproc_process.proc.finished, timeout=15000): quteproc_process.send_cmd(':quit') with pytest.raises(testprocess.ProcessExited): - quteproc_process.after_test(did_fail=False) + quteproc_process.after_test() @pytest.mark.parametrize('data, attrs', [