Add basic profiling capability for quteproc tests.
When --qute-profile-subprocs is given, we write a profile file for each qutebrowser invocation and also create prof/combined.pstats afterwards.
This commit is contained in:
parent
6d11e9ffd8
commit
02f367a308
1
.gitignore
vendored
1
.gitignore
vendored
@ -29,4 +29,5 @@ __pycache__
|
|||||||
/.cache
|
/.cache
|
||||||
/.testmondata
|
/.testmondata
|
||||||
/.hypothesis
|
/.hypothesis
|
||||||
|
/prof
|
||||||
TODO
|
TODO
|
||||||
|
@ -151,6 +151,8 @@ def pytest_addoption(parser):
|
|||||||
help='Disable xvfb in tests.')
|
help='Disable xvfb in tests.')
|
||||||
parser.addoption('--qute-delay', action='store', default=0, type=int,
|
parser.addoption('--qute-delay', action='store', default=0, type=int,
|
||||||
help="Delay between qutebrowser commands.")
|
help="Delay between qutebrowser commands.")
|
||||||
|
parser.addoption('--qute-profile-subprocs', action='store_true',
|
||||||
|
default=False, help="Run cProfile for subprocesses.")
|
||||||
|
|
||||||
|
|
||||||
def pytest_configure(config):
|
def pytest_configure(config):
|
||||||
|
@ -21,6 +21,28 @@
|
|||||||
|
|
||||||
"""Things needed for integration testing."""
|
"""Things needed for integration testing."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import pstats
|
||||||
|
|
||||||
from webserver import httpbin, httpbin_after_test, ssl_server
|
from webserver import httpbin, httpbin_after_test, ssl_server
|
||||||
from quteprocess import quteproc_process, quteproc, quteproc_new
|
from quteprocess import quteproc_process, quteproc, quteproc_new
|
||||||
from testprocess import pytest_runtest_makereport
|
from testprocess import pytest_runtest_makereport
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_configure(config):
|
||||||
|
"""Remove old profile files."""
|
||||||
|
if config.getoption('--qute-profile-subprocs'):
|
||||||
|
try:
|
||||||
|
shutil.rmtree('prof')
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def pytest_unconfigure(config):
|
||||||
|
"""Combine profiles."""
|
||||||
|
if config.getoption('--qute-profile-subprocs'):
|
||||||
|
stats = pstats.Stats()
|
||||||
|
for fn in os.listdir('prof'):
|
||||||
|
stats.add(os.path.join('prof', fn))
|
||||||
|
stats.dump_stats(os.path.join('prof', 'combined.pstats'))
|
||||||
|
@ -28,6 +28,7 @@ import datetime
|
|||||||
import logging
|
import logging
|
||||||
import tempfile
|
import tempfile
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import itertools
|
||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
import pytest
|
import pytest
|
||||||
@ -39,6 +40,9 @@ from qutebrowser.utils import log, utils
|
|||||||
from helpers import utils as testutils
|
from helpers import utils as testutils
|
||||||
|
|
||||||
|
|
||||||
|
instance_counter = itertools.count()
|
||||||
|
|
||||||
|
|
||||||
def is_ignored_qt_message(message):
|
def is_ignored_qt_message(message):
|
||||||
"""Check if the message is listed in qt_log_ignore."""
|
"""Check if the message is listed in qt_log_ignore."""
|
||||||
# pylint: disable=no-member
|
# pylint: disable=no-member
|
||||||
@ -124,6 +128,9 @@ class QuteProc(testprocess.Process):
|
|||||||
basedir: The base directory for this instance.
|
basedir: The base directory for this instance.
|
||||||
_focus_ready: Whether the main window got focused.
|
_focus_ready: Whether the main window got focused.
|
||||||
_load_ready: Whether the about:blank page got loaded.
|
_load_ready: Whether the about:blank page got loaded.
|
||||||
|
_profile: If True, do profiling of the subprocesses.
|
||||||
|
_instance_id: An unique ID for this QuteProc instance
|
||||||
|
_run_counter: A counter to get an unique ID for each run.
|
||||||
|
|
||||||
Signals:
|
Signals:
|
||||||
got_error: Emitted when there was an error log line.
|
got_error: Emitted when there was an error log line.
|
||||||
@ -134,14 +141,17 @@ class QuteProc(testprocess.Process):
|
|||||||
KEYS = ['timestamp', 'loglevel', 'category', 'module', 'function', 'line',
|
KEYS = ['timestamp', 'loglevel', 'category', 'module', 'function', 'line',
|
||||||
'message']
|
'message']
|
||||||
|
|
||||||
def __init__(self, httpbin, delay, parent=None):
|
def __init__(self, httpbin, delay, *, profile=False, parent=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
|
self._profile = profile
|
||||||
self._delay = delay
|
self._delay = delay
|
||||||
self._httpbin = httpbin
|
self._httpbin = httpbin
|
||||||
self._ipc_socket = None
|
self._ipc_socket = None
|
||||||
self.basedir = None
|
self.basedir = None
|
||||||
self._focus_ready = False
|
self._focus_ready = False
|
||||||
self._load_ready = False
|
self._load_ready = False
|
||||||
|
self._instance_id = next(instance_counter)
|
||||||
|
self._run_counter = itertools.count()
|
||||||
|
|
||||||
def _is_ready(self, what):
|
def _is_ready(self, what):
|
||||||
"""Called by _parse_line if loading/focusing is done.
|
"""Called by _parse_line if loading/focusing is done.
|
||||||
@ -201,12 +211,28 @@ class QuteProc(testprocess.Process):
|
|||||||
|
|
||||||
def _executable_args(self):
|
def _executable_args(self):
|
||||||
if hasattr(sys, 'frozen'):
|
if hasattr(sys, 'frozen'):
|
||||||
|
if self._profile:
|
||||||
|
raise Exception("Can't profile with sys.frozen!")
|
||||||
executable = os.path.join(os.path.dirname(sys.executable),
|
executable = os.path.join(os.path.dirname(sys.executable),
|
||||||
'qutebrowser')
|
'qutebrowser')
|
||||||
args = []
|
args = []
|
||||||
else:
|
else:
|
||||||
executable = sys.executable
|
executable = sys.executable
|
||||||
args = ['-m', 'qutebrowser']
|
if self._profile:
|
||||||
|
profile_dir = os.path.join(os.getcwd(), 'prof')
|
||||||
|
profile_id = '{}_{}'.format(self._instance_id,
|
||||||
|
next(self._run_counter))
|
||||||
|
profile_file = os.path.join(profile_dir,
|
||||||
|
'{}.pstats'.format(profile_id))
|
||||||
|
try:
|
||||||
|
os.mkdir(profile_dir)
|
||||||
|
except FileExistsError:
|
||||||
|
pass
|
||||||
|
args = [os.path.join('scripts', 'dev', 'run_profile.py'),
|
||||||
|
'--profile-tool', 'none',
|
||||||
|
'--profile-file', profile_file]
|
||||||
|
else:
|
||||||
|
args = ['-m', 'qutebrowser']
|
||||||
return executable, args
|
return executable, args
|
||||||
|
|
||||||
def _default_args(self):
|
def _default_args(self):
|
||||||
@ -397,7 +423,8 @@ class QuteProc(testprocess.Process):
|
|||||||
def quteproc_process(qapp, httpbin, request):
|
def quteproc_process(qapp, httpbin, request):
|
||||||
"""Fixture for qutebrowser process which is started once per file."""
|
"""Fixture for qutebrowser process which is started once per file."""
|
||||||
delay = request.config.getoption('--qute-delay')
|
delay = request.config.getoption('--qute-delay')
|
||||||
proc = QuteProc(httpbin, delay)
|
profile = request.config.getoption('--qute-profile-subprocs')
|
||||||
|
proc = QuteProc(httpbin, delay, profile=profile)
|
||||||
proc.start()
|
proc.start()
|
||||||
yield proc
|
yield proc
|
||||||
proc.terminate()
|
proc.terminate()
|
||||||
@ -416,7 +443,8 @@ def quteproc(quteproc_process, httpbin, request):
|
|||||||
def quteproc_new(qapp, httpbin, request):
|
def quteproc_new(qapp, httpbin, request):
|
||||||
"""Per-test qutebrowser process to test invocations."""
|
"""Per-test qutebrowser process to test invocations."""
|
||||||
delay = request.config.getoption('--qute-delay')
|
delay = request.config.getoption('--qute-delay')
|
||||||
proc = QuteProc(httpbin, delay)
|
profile = request.config.getoption('--qute-profile-subprocs')
|
||||||
|
proc = QuteProc(httpbin, delay, profile=profile)
|
||||||
request.node._quteproc_log = proc.captured_log
|
request.node._quteproc_log = proc.captured_log
|
||||||
# Not calling before_test here as that would start the process
|
# Not calling before_test here as that would start the process
|
||||||
yield proc
|
yield proc
|
||||||
|
Loading…
Reference in New Issue
Block a user